-
-
題意:你的鍵盤有\(k\)個按鍵,矩陣\(T_{i,j}\),表示第\(i\)個鍵和第\(j\)個鍵之間的輸入頻率,並帶有一個\(L\)的修正,\(T\)的每一行都是非遞減的,現在你忘了你的密碼,但是你知道你密碼的相鄰兩個字符的輸入頻率,你需要構造一個序列,這個序列需滿足\(T_{S_i,S_{i+1}}-L \le P_i \le T_{S_iS_{i+1}}+L\),問你一共可以構造多少可能的序列並對\(10^9+7\)取模。
-
題解:對於\(P_1\),我們要現在\(T\)中找一個合法的\({i,j}\),那么對於\(P_2\),我們就只能考慮\(j,x\),也就是說第一個位置必須是\(j\),那這很明顯是可以狀態轉移的,所以這題我們考慮dp。比如說對於\(P_1\)我們找到了\(i,j\),那么\(P_2\)的貢獻之一就可以由\(i,j\)轉移而來,但是不好寫,我們反着來,先找\(P_n\),因為這樣如果我們有合法的位置,因為上一層算過了,就能直接轉移到這一層來。具體轉移就是看每一行,二分找一個滿足\(T_{S_i,S_{i+1}}-L \le P_i \le T_{S_iS_{i+1}}+L\)的區間,說明我們可以從區間內的上一層的這些行轉移過來。設\(dp[i][j]\)表示第\(P_i\)個間隔,第\(j\)行的所有可能序列數,假如\(pos1,pos2\)分別表示第\(j\)行合法的區間的左端點和右端點,所以有:\(dp[i][j]=\sum_{k=pos1}^{pos2}dp[i-1][k]\).這個復雜度是\(O(k)\)的,但因為區間是連續的所以可以用前綴和優化,操作之后\(dp[i][j]\)表示第\(P_i\)個間隔,前\(j\)行的所有可能序列數。
-
代碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int k,L; int T[770][770]; int n; int P[N]; ll dp[100010][800]; int main() { scanf("%d %d",&k,&L); for(int i=1;i<=k;++i){ for(int j=1;j<=k;++j){ scanf("%d",&T[i][j]); } } scanf("%d",&n); for(int i=1;i<n;++i){ scanf("%d",&P[i]); } for(int i=1;i<=k;++i) dp[n][i]=i; for(int i=n-1;i>=1;--i){ for(int j=1;j<=k;++j){ dp[i][j]=dp[i][j-1]; //前綴和 int pos1=upper_bound(T[j]+1,T[j]+1+k,P[i]+L)-T[j]; int pos2=lower_bound(T[j]+1,T[j]+1+k,P[i]-L)-T[j]; ll res1,res2; res1=dp[i+1][pos1-1]; res2=dp[i+1][pos2-1]; dp[i][j]=(dp[i][j]+res1-res2+mod)%mod; } } printf("%lld\n",dp[1][k]); return 0; }