题解:CF755F PolandBall and Gifts

· · 题解

题解区的复杂度分析都好奇怪啊,来个正确的复杂度分析!

题目大意

题目翻译已经足够清晰了。

题目分析

首先,建边 i\rightarrow p_i,则每个点的出度、入度都为 1,故图由若干个环组成。

首先考虑第一问:求花费 k 得到最大的贡献。

显然,对于长度为 len 的环,可以花费 i\le \lfloor\frac{len}{2}\rfloor 的代价,获得 2i 的贡献,如果 len 是奇数,还可以在此基础上再花费 1 的代价,获得 1 的贡献。直接贪心即可,复杂度 O(n)

然后就是第二问:花费 k 能得到的最小代价。

对于长度为 len 的环,第一次花费 1 一定会产生 2 的代价。然后第 2\sim len-1 次花费会产生 1 的代价,第 len 次产生 0 的代价。

所以如果能用若干个环恰好拼出 k 答案就是 k,否则是 k+1

问题变为 len_i 能否拼出 k,典型的背包问题。

采用二进制分组,对于种类数 $x$,最坏的情况下数量平均,二进制分组后物品数量应为 $O(x\log\frac{n}{x^2})$,我在 geogebra 上输入该函数并发现最差复杂度应为 $O(\sqrt n)$,至少在 $n\le 10^6$ 时这个结论是正确的,欢迎数学大佬来证明或指错。 所以再使用 bitset 背包,复杂度 $O(\frac{n\sqrt n}{w})$。 题解那些不认真分析复杂度的都能有高赞? ```cpp #include<bits/stdc++.h> #define rep(x,y,z) for(int x=(y);x<=(z);x++) #define per(x,y,z) for(int x=(y);x>=(z);x--) #define repn(x) rep(x,1,n) #define repm(x) rep(x,1,m) inline int read(){int s=0,w=1;char c=getchar();while(c<48||c>57) {if(c=='-') w=-1;c=getchar();}while(c>=48&&c<=57)s=(s<<1)+(s<<3)+c-48,c=getchar();return s*w;} using namespace std; const int N =1e6+5; int a[N],n,k,m,p[N]; bool v[N]; inline void dfs(int x,int &w){ w++,v[x]=1; if(!v[p[x]])dfs(p[x],w); } inline int solve1(int k){ int ans=0; repm(i){ int w=a[i]/2; ans+=min(w,k)*2,k-=min(w,k); if(!k)return ans; } repm(i)if(a[i]&1){ k--,ans++; if(!k)return ans; } return ans; } int c[N]; bitset<500001>B; inline int solve2(int k){ int pr=k; k=min(k,n-k),B[0]=1; repm(i)c[a[i]]++; rep(i,1,k)if(c[i]){ int kk=1,w=i; while(1){ if(c[i]>=kk)B=B|(B<<w),c[i]-=kk; else { B=B|(B<<(c[i]*i)); break; } kk<<=1,w<<=1; } } return pr+(!B[k]); } int main(){ n=read(),k=read(); repn(i)p[i]=read(); repn(i)if(!v[i])dfs(i,a[++m]); cout <<solve2(k)<<" "<<solve1(k)<<'\n'; return 0; } ```