1. 程式人生 > >[codechef MEXDIV]Mex division

[codechef MEXDIV]Mex division

++ 全部 count div name vision cnblogs bre 裏的

直接用set+dp水過去了。。。

/*
設dp[i]表示前i個做劃分滿足條件的方案數
有一個顯然的轉移方程dp[i]=sigma(dp[j])  t<=j<=i-1
其中t是滿足mex(a[t..i-1])<=k的最小的t 
然後我們現在是想得到,對於每個位置這個t是多少,如果知道了這個,就可以很容易的轉移了

首先,考慮,如果k>n,那麽不管怎麽選,都是會滿足條件的啊!(就算把所有的數都選出來,mex也就是k吧)
所以對於k>n,直接輸出2^(n-1)
當然k=0的時候特判一下,這樣每個i對應的t都是存在的,至少有一個i 現在考慮k<=n的情況,直接用一個multiset記錄[1,k]裏的數的出現情況 首先,把[0,k]全部都放進multiset,遇到一個新的數,就erase,查詢集合裏最小的數就是mex 假設現在想得到第i個位置的t,上次求得的i-1對應的t是t‘ 那麽如果a[i]已經在集合中被刪去了,那這次對應的肯定還是t‘ 如果a[i]沒有從集合中刪去,就把a[i]從集合中刪去(當然,如果a[i]>k直接不用管) 考慮到隨著i的增大,t肯定是往右走的 如果刪去以後集合是空集了,那t‘就需要右移了,直到出現一個[0,k]之間的,並且在a[t+1..i]沒有出現過的數 是否出現過,只需要記一個右邊第一個跟它相等的數就可以了
*/ #include<bits/stdc++.h> using namespace std; const int maxn=500005; int a[maxn]; int t[maxn]; int e[maxn]; long long dp[maxn]; long long predp[maxn]; pair<int,int> p[maxn]; set<int> S; const long long md=1000000007; long long fp(long long a,long long k) { long long res=1; a%=md;
while(k) { if(k&1)res=res*a%md; a=a*a%md; k>>=1; } return res; } int main() { int n,k; scanf("%d%d",&n,&k); for (int i=1;i<=n;i++) scanf("%d",&a[i]); if (k>n) { printf("%lld",fp(2,n-1)); return 0; }
if (k==0) { bool yl=false; for (int i=1;i<=n;i++) { if (a[i]==0) { yl=true; break; } } if (yl) printf("0\n"); else printf("%lld",fp(2,n-1)); return 0; } S.clear(); for (int i=0;i<=k;i++) S.insert(i); for (int i=1;i<=n;i++) { p[i].first=a[i]; p[i].second=i; } sort(p+1,p+1+n); p[n+1].first=-1; for (int i=1;i<=n;i++) { if (p[i+1].first==p[i].first) e[p[i].second]=p[i+1].second; else e[p[i].second]=-1; } int now=1; t[1]=1; if (a[1]<=k) S.erase(a[1]); for (int i=2;i<=n;i++) { if (a[i]>k || !S.count(a[i])) t[i]=t[i-1]; else { S.erase(a[i]); while (S.empty()) { if (a[now]<=k && (e[now]==-1||e[now]>i)) { S.insert(a[now]); } now++; } t[i]=now; } } dp[0]=1; predp[0]=0; predp[1]=1; for (int i=1;i<=n;i++) { // dp[t[i]-1]...dp[i-1] dp[i]=((predp[i]-predp[t[i]-1])%md+md)%md; predp[i+1]=(predp[i]+dp[i])%md; } printf("%lld\n",dp[n]); return 0; }

[codechef MEXDIV]Mex division