题意:给你一个礼物的置换。有k个人忘带了礼物。一个人无法获得礼物为他自己没有带礼物或给他带礼物的那个人没有带礼物。求选择k个人,没有获得礼物的人数的最小值和最大值。
n,k<=1e6.
标程:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 using namespace std; 5 int read() 6 { 7 int x=0,f=1;char ch=getchar(); 8 while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} 9 while (ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); 10 return x*f; 11 } 12 const int inf=0x3f3f3f3f; 13 const int N=1000005; 14 int n,k,m,sum,tot,cnt,vis[N],cir[N],dp[N],a[N],Max,Min,len,v[N],t; 15 int main() 16 { 17 n=read();k=read();sum=n; 18 for (int i=1;i<=n;i++) a[i]=read(); 19 for (int i=1;i<=n;i++) 20 if (!vis[i]) 21 { 22 cnt=1; 23 for (int j=a[i];j!=i;j=a[j]) cnt++,vis[j]=1; 24 cir[++tot]=cnt;sum-=(cnt&1); 25 } 26 sort(cir+1,cir+tot+1); 27 if (k*2<=sum) Max=k*2;else Max=min(sum+k-sum/2,n); 28 29 for (int i=1,j;i<=tot;i++) 30 if (cir[i]!=cir[i+1]) 31 { 32 len++; 33 for (j=1;len;j<<=1) t=min(len,j),v[++m]=t*cir[i],len-=t; 34 }else len++; 35 dp[0]=1; 36 for (int i=1;i<=m;i++) 37 for (int j=k;j>=v[i];j--) 38 dp[j]=dp[j]|dp[j-v[i]]; 39 Min=k+(!dp[k]); 40 printf("%d %d\n",Min,Max); 41 return 0; 42 }
题解:贪心+背包
提取若干个独立的礼物置换群。
对于一个置换:选k个人的最小值是k+1,最大值是2k。出界了和环长取min。
那么对于所有的置换,最大值就是每个环中尽可能两个两个取,没有两个可取了就一个一个取。
最小值,如果k恰好能表示成若干个环长的和,那么答案就是k,反之会有多余,是k+1。用多重背包算一下能否装满k个即可。用二进制优化做到nlogn*n^0.5(不同的环长约有n^0.5个)。(当然还有单调队列优化,可惜我不会)