C. Set or Decrease
题意:给出一组数,可以对每个数进行以下操作:1) 将它-1. 2)将它变成数组中另一个数。给出k,问至少作多少次操作能使数组和小于等于k。
解:首先贪心地想,要么把最小的数变得更小,然后令其他数等于它,要么每个减1。再想想每个减一没有让它变成最小数合算。现在题里有两个变量,一是令最小的数减小x,一是将i个数粘贴为最小值,直到sum≤k。试图二分,但这两个量是关联的,非线性。试图三分,但很麻烦,不是每个值都能解。一般这么麻烦是可以直接算的。算式见代码。最后直接枚举每个i,O(n)求解。顺带一提最后表达式写出来是x+1/x形式,应该是可以三分的qwq
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxx 200005 #define eps 0.00000001 #define inf 0x7fffffff //#define int long long ll n,k; ll a[maxx],sum[maxx]={0}; signed main() { int T; scanf("%d",&T); while(T--){ scanf("%lld%lld",&n,&k); for(int i=1;i<=n;i++) scanf("%lld",&a[i]); sort(a+1,a+n+1); for(int i=1;i<=n;i++) sum[i]=a[i]+sum[i-1]; if(sum[n]<=k) { printf("0\n"); continue; } ll ans=1e18; for(int i=0;i<=n-1;i++){ ll temp=a[1]+ceil(-(k-sum[n-i]+sum[1])/(i+1.0)); if(temp<0) temp=0; ans=min(ans,temp+i); } printf("%lld\n",ans); } return 0; } // suppose that the deletion num is x, and turn i of the n nums to a[1]-x; // (a[1]-x)*(i+1)+sum[2...n-i]<=k // x>=a[1]-((k-sum[2...n-i])/(i+1)) // ans=m+xView Code
D. Shuffle
题意:给出一个01串,每次选一个有k个1的连续区间重新排列组合,问最终有几种结果。
解:排 列 组 合。显然每次选择包含k个1的最长区间先算个组合数,但这样算下来肯定有重复。考虑重复,假设两个区间为 ***** ,那么它们的最大重叠区间会有相同的排列,具体来说,就是从全部重叠个数中选其中1的数量。由于是两个相邻区间,所以重叠部分会有k-1个1。注意模数
代码:
#include<bits/stdc++.h> using namespace std; #define ll long long #define maxx 5005 #define eps 0.00000001 #define inf 0x7fffffff #define mod 998244353 //#define int long long ll n,k; ll C[maxx][maxx]={0}; ll pos[maxx]={0},cnt=0; char s[maxx]; void init(){ C[0][0]=1; for(int i=1;i<maxx;i++){ C[i][0]=1; for(int j=1;j<maxx;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod; } } signed main() { init(); scanf("%d%d",&n,&k); scanf("%s",s+1); for(int i=1;i<=n;i++){ if(s[i]=='1') pos[++cnt]=i; } if(k==0||k>cnt){ printf("1\n"); return 0; } ll ans=0; ll front=1; for(int i=1;i<=cnt-k+1;i++){ ll p1=pos[i-1]+1; ll p2=i+k>cnt?n:pos[i+k]-1; ans=(ans+C[p2-p1+1][k])%mod; if(i!=1) ans=(ans+mod-C[pos[i+k-1]-front-1][k-1])%mod; front=pos[i]; } printf("%lld\n",ans); return 0; }View Code