CCPC2020綿陽 L題 Lottery
阿新 • • 發佈:2020-11-05
Pro:
https://pintia.cn/problem-sets/1322796904464203776/problems/1322798545527595019
Sol:
首先可以考慮一個多重揹包的二進位制拆解
注意,這裡的二進位制拆解指的是拆成\(O(logn)\)的\(2^k\)的形式
實現的話就是先\(1,2,4,8,16...\)能拆就拆
對於剩下的部分直接拆成它在二進位制下每一位為1的位即可
然後問題轉換成了每個物品個數都不超過2的一個問題
繼而發現
假設序列形如
\(112121012111.....\)
每一段連續的1和2是獨立的!
因為他們的最大和也不可能超過\(2^(k+1)\)
所以每一段單獨dp,把答案乘起來即可。
#include<bits/stdc++.h> #define N 1100000 #define db double #define ll long long #define ldb long double #define ull unsigned long long using namespace std; const int h=3,ki=149,mo=1e9+7; inline int inc(int x,int k){x+=k;return x<mo?x:x-mo;} inline int dec(int x,int k){x-=k;return x>=0?x:x+mo;} inline int read() { char ch=0;int x=0,flag=1; while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0',ch=getchar();} return x*flag; } inline void write(int x) { if(!x)return (void)putchar(48); if(x<0)putchar(45),x=-x; int len=0,p[20]; while(x)p[++len]=x%10,x/=10; for(int i=len;i>=1;i--)putchar(p[i]+48); } const db eps=1e-7,inf=1e9+7,pi=acos(-1); inline db Read(){db x;scanf("%lf",&x);return x;} inline void Write(db x){printf("%lf",x);} map<int,int>mp; map<int,int>::iterator it; int main() { int t=read(); for(int o=1;o<=t;o++) { int n=read(),ans=1;mp.clear(); for(int i=1;i<=n;i++){int x=read(),k=read();mp[x]+=k;} for(it=mp.begin();it!=mp.end();it++) { int x=it->first,k=it->second;mp[x]=0; for(int i=0;(1<<i)<=k;i++)mp[x+i]++,k-=(1<<i); for(int i=0;(1<<i)<=k;i++)if((1<<i)&k)mp[x+i]++,k-=(1<<i); } int f0=1,f1=0; for(it=mp.begin();it!=mp.end();it++) { int x=it->first,k=it->second; if(!mp[x-1])ans=1ll*ans*inc(f0,f1)%mo,f0=1,f1=0; f1=(k==1)?f1:inc(f0,f1);f0=inc(f0,f0); } ans=1ll*ans*inc(f0,f1)%mo; printf("Case #%d: %d\n",o,(ans%mo+mo)%mo); } }
這個題還有另外一個做法
如下
但我還不太清楚具體怎麼實現(怎麼比較當前的和和下一個數字的大小啊qwq)