B. Kevin and Permutation
题意:构造一个n的排列,使得 min |a[i+1]-a[i]| 最大。
解:数字都是两两比较,所以最大值不会超过n/2。考虑1, 1+n/2, 2, 2+n/2...这个序列中1+n/2-2=n/2-1,不好。所以改成1+n/2, 1, 2+n/2, 2...
C1. Make Nonzero Sum (easy version)
题意:规定在数列上一种计算s=a1-a2+a3-a4+...现给出一个数列,要求将其分为若干组,使得每组运算结果之和为0.
解:首先有奇数个数肯定分不出。一开始想的把偶数位的数正负号变一下,加到为0为一组,然后发现是要每组算一个数,再加起来等于0。也就是可以正负抵消。介于值只有1和-1两种,所以把它们按顺序两两配对。相同的分在一组,凑出一个0;不同的分两组,加起来也是0.
C2. Make Nonzero Sum (hard version)
题意:在上一题的基础上给数组加入0。要求不变。
解:0很好呀,可以看作没有。0造成的影响是比如原来[1, 1]分成一组,现在[1, 0, 1]就不能分成一组了,但[1, 0, 0, 1]还是可以分成一组。对于相邻的两个1和-1不影响。那么考虑[1, 0, 1]怎么处理,看一眼样例,很好样例已经把答案写上了。分一个0给后面的1,让它变成-1。最后进行一个大的分类讨论。
代码:
#include <bits/stdc++.h> using namespace std; #define maxx 200005 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 998244353 int a[maxx]={0}; signed main() { int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } int sum=0; for(int i=1;i<=n;i++){ sum+=a[i]; } if(sum%2){ printf("-1\n"); continue; } vector<pair<int,int> > ans; int pre=0,pos=1; while(pos<=n){ if(a[pos]!=0){ if(pre==0){ pre=pos; } else{ if(a[pos]==a[pre]&&(pos-pre-1)%2){ ans.push_back(make_pair(pre,pre)); ans.push_back(make_pair(pre+1,pos)); } if(a[pos]==a[pre]&&(pos-pre-1)%2==0){ ans.push_back(make_pair(pre,pos)); } if(a[pos]!=a[pre]){ ans.push_back(make_pair(pre,pre)); ans.push_back(make_pair(pos,pos)); } pre=0; } } pos++; } vector<pair<int,int> > ans2; int end=0; for(auto [l,r]:ans){ if(l-end>1){ ans2.push_back(make_pair(end+1,l-1)); } ans2.push_back(make_pair(l,r)); end=r; } if(n-end>0) ans2.push_back(make_pair(end+1,n)); printf("%d\n",ans2.size()); for(auto [l,r]:ans2){ printf("%d %d\n",l,r); } } return 0; }
D. Factorial Divisibility
题意:给出x和n个数,设这n个数为a1, a2, a3...an,问 a1!+a2!+a3!+...an! 是否能被 x! 整除。
解:试图对 a1!+a2!+a3!+...an! 这个式子进行一些处理,因为要除以 x! ,所以先提取出2,设num[i]为数字 i 的个数,式子变成 num[1]/2+num[2]+3*(num[3]+4*(...))),num[1]/2必须是整数;同理,接着提取出3,式子变成 (num[1]/2+num[2])/3+num[3]+4*(...)),(num[1]/2+num[2])/3必须是整数,剩下的同理,一直提取到x-1,看看每次是否都能除尽。如果有一个不能,就无法整除;反正可以整除。x-1之后不用检查,x!肯定能整除x!。
代码:
#include <bits/stdc++.h> using namespace std; #define maxx 500005 #define maxn 25 #define maxm 205 #define ll long long #define inf 1000000009 #define mod 998244353 int num[maxx]={0}; signed main() { // int T; // scanf("%d",&T); // while(T--){ // // } int n,x; scanf("%d%d",&n,&x); for(int i=1;i<=n;i++){ int t; scanf("%d",&t); num[t]++; } int ans=num[1]; for(int i=1;i<x;i++){ if(ans%(i+1)==0){ ans=ans/(i+1)+num[i+1]; } else{ printf("No\n"); return 0; } } printf("Yes\n"); return 0; } //(num[1]+2*(num[2]+3*(num[3]+4*(...))))/2..x ////num[1]/2+num[2]+3*(num[3]+4*(...))))/3...x ////(num[1]/2+num[2])/3+num[3]+4*(...)))/4...x
E. Wish I Knew How to Sort
Wish I Knew How to AC
题意:给出一个01序列,每次随机挑俩数a[l]和a[r](l<r),如果 a[l]<a[r] 就交换它们的位置。操作若干次(即使不交换也算作一次操作),如果要使最终结果有序,求执行几次操作的期望。
解:期望题,看到n的范围为2e5,先设个dp[i]。先算了下样例,想设dp[i]为有i个逆序对时的期望,然后进行了一堆无效观察,决定去看一点题解。假设有g个0,设dp[i]为前g个数中有i个0时的期望,显然dp[g]=0。或者换一种说法是换i次能使其有序,这个状态好转移多了。下一次选中的两个数要命能交换,要么不能,设p为能交换的概率。dp[i]=p*dp[i+1]+(1-p)*dp[i]+1,最后那个1是因为本次也是选了一次的。