黑客(续) (压位高精+状压dp)
黑客(續)
- 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; }總結
以上是生活随笔為你收集整理的黑客(续) (压位高精+状压dp)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux系统电脑(linux系统 电脑
- 下一篇: 朝鲜时蔬(分数据点写算法+毒瘤数学)