黑客(续)
- description
- solution
- code
description
【问题描述】
在破解了世界首富 Bychaha 的银行账户后,知名黑客 pks 发现,要得到
Bychaha 的全部财产,必须再破解一道密码。
作为客户账户安全的最后一道防线,这一次的密码将由长达 N 位的数码组
成,每一位的数码范围为[1,K]。
pks 想要估算自己破解密码的大致时间,所以他想要你帮他快速计算出,总
共有多少种满足条件的密码,同时,pks 还对每一种密码视为十进制数之后求
和的结果很感兴趣,希望你也能告诉他。
当然,作为世界知名黑客,pks 不会傻傻的枚举,他已经从银行系统中窃取
到了关于密码的 M 个信息,每个信息由两个数字𝑎,𝑏表示,代表数码𝑎不会出
现在数码𝑏之前。
【输入格式】
第一行三个整数 N,M,K,含义如题面所述。
接下来 M 行,每行两个数𝑎,𝑏。
【输出格式】
输出共两行。
第一行一个整数表示有多少种满足条件的密码。
第二行一个整数表示所有密码视为十进制数之后求和的结果。
【样例输入输出】
4 4 3
1 1
1 2
2 2
3 1
7
19020
【数据规模与约定】
对于 20%的数据, 𝑁 ≤ 6
对于 30%的数据, 𝑁 ≤ 50
对于 40%的数据, 𝑁 ≤ 200
另有 50%的数据,𝑀 = 0
对于 100%的数据,1 ≤ 𝑁 ≤ 500,0 ≤ 𝑀 ≤ 100 , K ≤ 9
solution
当你发现没有模数的那一刻,可能心脏已经跳脱了
这该死的 甜美 大数还是来了
N<=6
直接暴力搜索,每一位选择[1,k][1,k][1,k]最后组合出来,再判断
m=0
没有限制条件,摆明了说个数就是knk^nkn,答案就是每一个数字固定在每一位的数值乘以这种形式的密码个数
直接脱光了说:答案都跟你说了,你写出大数就有一半的分,再加上最原始的暴力,就算是很不错的分了
没想到自己在考场上真的把大数敲出来了
最后就是正解了
很简单,可以预处理出已经出现了sss集合内的数字,且是合法的,能接在后面的所有数字
设fi,s/gi,s:f_{i,s}/g_{i,s}:fi,s/gi,s: 在第iii位已经出现了sss集合内的数字的所有密码和/个数
这个状压转移应该都能写吧
但是会发现,答案特别大,就需要——压位高精
这里好巧不巧选择压171717位(1e171e171e17)就能过
之前以为压位很难,结果发现就是将是十七位当成一位,(mod1e17)\pmod {1e17}(mod1e17)就行
最后输出再每一位扩充出来%17lld\text{\%17lld}%17lld,171717是表示输出是171717位不够的自动填充000
当然注意最高的一位不能扩充,ta是多少位就是多少位
所以这道题的难点是压位高精??
code
#pragma GCC optimize(2)
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
#define int long long
const int mod = 1e17;
int n, m, k;
bool vis[10][10];
vector < int > nxt[1 << 9];struct Int {int len;int c[60];Int(){ memset( c, 0, sizeof( c ) ); }Int operator * ( int x ) {Int ans;ans.len = len;int add = 0;for( int i = 1;i <= len;i ++ ) {int ret = c[i] * x + add;ans.c[i] = ret % mod;add = ret / mod;}if( add ) ans.c[++ ans.len] = add;return ans;}Int operator + ( Int x ) {Int ans;int ip = 1;while( ip <= x.len or ip <= len ) {int ret = ans.c[ip] + x.c[ip] + c[ip];if( ret >= mod ) {ans.c[ip + 1] ++;ans.c[ip] = ret - mod;}elseans.c[ip] = ret;++ ip;}ans.len = ip;if( ! ans.c[ans.len] ) ans.len --;return ans;}void print() {printf( "%lld", c[len] );for( int i = len - 1;i > 0;i -- ) printf( "%017lld", c[i] );printf( "\n" );}void init() {for( int i = 1;i <= len;i ++ ) c[i] = 0;len = 0;}
}f[2][1 << 9], g[2][1 << 9];signed main() {scanf( "%lld %lld %lld", &n, &m, &k );for( int i = 1, a, b;i <= m;i ++ ) {scanf( "%lld %lld", &a, &b );vis[a][b] = 1;}int lim = 1 << k;for( int s = 0;s < lim;s ++ )for( int i = 1;i <= k;i ++ ) {for( int j = 1;j <= k;j ++ )if( ( 1 << j - 1 & s ) and vis[j][i] ) goto opt;nxt[s].push_back( i );opt :;}g[0][0].c[g[0][0].len = 1] = 1;for( int i = 1;i <= n;i ++ ) {int o = i & 1;for( int s = 0;s < lim;s ++ )for( int j : nxt[s] ) {f[o][1 << j - 1 | s] = f[o][1 << j - 1 | s] + f[o ^ 1][s] * 10 + g[o ^ 1][s] * j;g[o][1 << j - 1 | s] = g[o][1 << j - 1 | s] + g[o ^ 1][s];}for( int s = 0;s < lim;s ++ )f[o ^ 1][s].init(), g[o ^ 1][s].init();}g[n & 1][0].c[1] = 0;for( int i = 1;i < lim;i ++ ) {f[n & 1][0] = f[n & 1][i] + f[n & 1][0];g[n & 1][0] = g[n & 1][i] + g[n & 1][0];}g[n & 1][0].print();f[n & 1][0].print();return 0;
}