Educational Codeforces Round 86 (Rated for Div. 2)
A. Road To Zero
题意:给两个数x,y,你可以花费a的代价使得其中一个数+-1,也可以花费b的代价使得两个数都+-1,问最少需要多少代价使得两个数都变成0。
思路:如果xy异号或者其中一个为0,那么一定不会使用b操作,此时只使用a操作。如果xy同号,可以先用b操作使得二者其中一个为0,再使用a操作使得二者都为0,而如果2次a操作的代价比1次b操作的代价还要低,那么就不会用b操作,也等价于只用a操作。所以有两种情况,直接用a操作,先用b操作把xy中绝对值较小的那一个变为0再用a操作。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T; 18 int x,y,a,b; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(x);rd(y);rd(a);rd(b); 24 LL ans1=1ll*(abs(x)+abs(y))*a,ans2=1ll*abs(x)*b+1ll*abs(y-x)*a,ans3=1ll*abs(y)*b+1ll*abs(x-y)*a; 25 printf("%lld\n",min(ans1,min(ans2,ans3))); 26 } 27 return 0; 28 } 29 /**/
B. Binary Period
题意:给一个长t(1e4)的01串,往中间插入若干个0或者1,使得最终长度<=2t且周期最小。一个字符串周期为k(>0的整数)则需要使得所有i满足f[i+k]=f[i]。输出最终的字符串。
思路:放在b的位置肯定是特殊情况的构造题。容易发现,如果字符串全是0或者全是1,那么直接输出其本身即可,k取到1。而其他情况一定不是1,但是可以构造一个k取到2的情况。即长度为2t的0101交替出现的字符串,也用到了这个很常用的思路"每个长度为k的字符串对应原字符串一个字符,构造n个相同的串首尾相接"。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=105; 17 using namespace std; 18 int T; 19 char t[N]; 20 void check(char *s){ 21 int n=strlen(s); 22 for(int i=0;i<n-1;i++) 23 if(s[i] != s[i+1]){ 24 for(int j=0;j<n;j++)putchar('0'),putchar('1'); 25 puts("");return ; 26 } 27 printf("%s\n",t);return ; 28 } 29 int main(){ 30 // freopen("in.txt","r",stdin); 31 rd(T); 32 while(T--){ 33 scanf("%s",t);check(t); 34 } 35 return 0; 36 } 37 /**/
C. Yet Another Counting Problem
题意:T(100)组数据,给出a,b(200),q(500)次询问,每次询问l到r(1~1e18)之间满足x%a%b != x%b%a的x的数目。
思路:可以想到用前缀和的思想,问题转换为求1~l中满足题意的x。可以发现如果x与x+ab的情况完全相同,只需要看1~ab之间的数是否满足即可。对每组数据预处理出1~ab的情况即可。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T; 18 int a,b,q; 19 int s[205*205]; 20 LL get_ans(LL x){return (x/(a*b))*s[a*b] + s[x%(a*b)];} 21 int main(){ 22 // freopen("in.txt","r",stdin); 23 rd(T); 24 while(T--){ 25 rd(a);rd(b);rd(q);s[0]=0; 26 for(int i=1;i<=a*b;i++){ 27 s[i]=s[i-1]; 28 if(i%a%b != i%b%a)s[i]++; 29 } 30 for(int i=1;i<=q;i++){ 31 LL l,r;lrd(l);lrd(r); 32 if(a == b)printf("0 "); 33 else printf("%lld ",get_ans(r)-get_ans(l-1)); 34 } 35 puts(""); 36 } 37 return 0; 38 } 39 /**/
D. Multiple Testcases
题意:n(2e5)个不超过k(2e5)的正整数,划分为最少的组数,使得每组中大于等于i的元素不超过ci个。输出分组方案。
思路:我和答案的思路仍然不同,答案很优秀。
答案:设si表示初始数字中大于等于i的数字个数,那么si/ci上取整的最大值就是答案。如果组数取得小于这个值,那么在处理si/ci上取整最大时对应的ci时将无法分配。而这个值本身又一定可以符合题意。就类似于抽屉原理的方法分配即可。
我的思路:上限是n组,下限是1组,组少时能装下组多时便同样可以装下。于是进行二分。取定组数后判断时可以以抽屉原理类似的方式分配答案即可。(我在考场上用了毫无意义的堆,莫名增加了一个log的复杂度)。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 using namespace std; 18 int n,k,m[N],c[N]; 19 struct ghb{ 20 int id,val; 21 bool operator < (const ghb &o)const{ 22 return val < o.val; 23 } 24 }; 25 priority_queue<ghb>S; 26 int bel[N]; 27 bool check(int x){ 28 while(!S.empty())S.pop(); 29 for(int i=1;i<=x;i++)S.push((ghb){i,0}); 30 for(int i=n,j=k;i>=1;i--){ 31 while(j > m[i])j--; 32 ghb u=S.top();S.pop(); 33 if(u.val + c[j] <= 0)return 0; 34 S.push((ghb){u.id,u.val-1}); 35 bel[i]=u.id; 36 } 37 return 1; 38 } 39 vector<int>P[N]; 40 int main(){ 41 // freopen("in.txt","r",stdin); 42 rd(n);rd(k); 43 for(int i=1;i<=n;i++)rd(m[i]);sort(m+1,m+n+1); 44 for(int i=1;i<=k;i++)rd(c[i]); 45 int l=1,r=n; 46 while(l != r){ 47 int mid=l+r>>1; 48 if(check(mid))r=mid; 49 else l=mid+1; 50 } 51 check(l); 52 printf("%d\n",l); 53 for(int i=1;i<=n;i++)P[bel[i]].push_back(m[i]); 54 for(int i=1;i<=l;i++){ 55 printf("%d ",P[i].size()); 56 for(int j=0;j<P[i].size();j++) 57 printf("%d ",P[i][j]); 58 puts(""); 59 } 60 return 0; 61 } 62 /**/
E. Placing Rooks
题意:将n(2e5)个车放在n*n的棋盘上使得每个格子都可以被车攻击到,同时有k对车可以互相攻击。车就是象棋中的车。求方案数对998244353取模后的答案。
思路:最终的答案一定满足"每行都有一个车"或者"每列都有一个车"。如果不是的话必然会出现行和列的交点无法被攻击到。最终答案就是"每行都有车"+“每列都有车”-“每行每列都有车”。而"每行每列都有车"的方案数在k=0时为n!,k!=0时为0。特判k=0的情况,我们便可以只研究"每行都有车",最终*2即可。我们发现如果一列有x个车,那么会有x-1对相互攻击的车。可以发现最多有n-1对相互攻击的车。所以当k>=n时方案数为0。当x列有车时,容易发现相互攻击的车的对数为n-x。于是仅当有n-k列有车时,相互攻击的车的对数为k。我们只关心多少列有车,车怎样摆放都合法。可以转换为组合数的基本问题。每一行的车视为一个小球,小球之间不同,需要放到n-k个互不相同的篮子中,方案数可以用第二类斯特林数来求得。随后我会整理一下基本的组合数问题解决方法。
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=2e5+10; 17 const int mod=998244353; 18 using namespace std; 19 int n; 20 LL k; 21 int fac[N],inv[N]; 22 int fst(int x,int y){ 23 int ans=1; 24 while(y){ 25 if(y & 1)ans=1ll*ans*x%mod; 26 x=1ll*x*x%mod;y>>=1; 27 } 28 return ans; 29 } 30 int C(int x,int y){ 31 return 1ll*fac[x]*inv[y]%mod*inv[x-y]%mod; 32 } 33 int main(){ 34 // freopen("in.txt","r",stdin); 35 rd(n);lrd(k); 36 fac[0]=1;for(int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod; 37 inv[n]=fst(fac[n],mod-2);for(int i=n-1;i>=0;i--)inv[i]=1ll*(i+1)*inv[i+1]%mod; 38 if(k == 0)printf("%d\n",fac[n]); 39 else if(k >= n)printf("0\n"); 40 else { 41 int u=n-k; 42 int ans=0; 43 for(int i=0;i<=u;i++){ 44 int tmp=1ll*C(u,i)*fst(u-i,n)%mod; 45 if(i&1)ans=(ans-tmp+mod)%mod; 46 else ans=(ans+tmp)%mod; 47 } 48 printf("%d\n",1ll*ans*2%mod*C(n,u)%mod); 49 } 50 return 0; 51 } 52 /**/