CodeForces 755F PolandBall and Gifts
题意:n个人相互送礼物,每个人只会另外一个人(一定不会是自己),而且每个人都会被送礼物。满足以下条件的人可以收到礼物:1.自己送别人礼物;2.有人送自己礼物。现在需要让k个人的礼物无法送出,问最少和最多能有多少人收不到礼物。
题解:相互送礼物的人可以分成环,所以先把环找出来。对于最大值,贪心就好。最小值需要用到多重背包,但是直接的背包会因为复杂度太高而T掉,外加本题只需要求出多重背包的可行性,所以在别人那里学了一发二进制优化。
代码略搓。。。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 using namespace std; 6 int f[1000005]; 7 int find(int x){ 8 return f[x]==x?x:f[x]=find(f[x]); 9 } 10 int merge(int x,int y){ 11 int fx=find(x); 12 int fy=find(y); 13 f[fx]=fy; 14 } 15 int a[1000006]; 16 int s[1000006],tot; 17 int num[1000006]; 18 int b[1000006],c[1000006],cnt; 19 int d[1000006]; 20 int dp[1000006]; 21 int n,k; 22 int main(){ 23 scanf("%d%d",&n,&k); 24 for(int i=1;i<=n;i++) f[i]=i; 25 for(int i=1;i<=n;i++){ 26 scanf("%d",&a[i]); 27 merge(i,a[i]); 28 } 29 if(k==0){ 30 printf("0 0\n"); 31 return 0; 32 } 33 for(int i=1;i<=n;i++) num[find(i)]++; 34 for(int i=1;i<=n;i++){ 35 if(num[i]!=0){ 36 tot++; 37 s[tot]=num[i]; 38 } 39 } 40 sort(s+1,s+tot+1); 41 int mi=k,ma=0; 42 int p=k,res=n; 43 for(int i=1;i<=tot;i++){ 44 int kk=s[i]/2; 45 if(p>=kk){ 46 ma+=kk*2; 47 p-=kk; 48 res-=kk*2; 49 } 50 else{ 51 ma+=p*2; 52 p=0; 53 } 54 if(p==0||res==0)break; 55 } 56 ma+=min(p,res); 57 memset(dp,0,sizeof(dp)); 58 for(int i=1;i<=tot;i++){ 59 if(s[i]==s[i-1]) c[cnt]++; 60 else c[++cnt]=1,b[cnt]=s[i]; 61 } 62 tot=0; 63 for(int i=1;i<=cnt;i++){ 64 for(int j=1;c[i];j<<=1){ 65 int temp=min(c[i],j); 66 d[++tot]=b[i]*temp; 67 c[i]-=temp; 68 } 69 } 70 dp[0]=true; 71 for(int i=1;i<=tot;i++){ 72 for(int j=k-d[i];j>=0;j--) 73 if(dp[j]>0) dp[j+d[i]]=1; 74 } 75 if(!dp[k]) mi++; 76 printf("%d %d\n",mi,ma); 77 78 return 0; 79 }