【CF】 Educational Codeforces Round 56 (Rated for Div. 2)
ac被虐后,div2cf又惨遭横祸。。真的菜啊orz
cfcfcf
A题:给定一个数,一个点数2-7的筛子,给定询问x,回答一种可能的总和加起来为x的次数。
奇数-3之后当偶数考虑,x/2便为一种可能的次数。
#include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> using namespace std; int T; int main() { scanf("%d",&T); while(T--) { int ans = 0; int x; scanf("%d",&x); if(x<=3) { puts("1"); continue; } if(x&1) x-=3,ans++; ans += x/2; printf("%d\n",ans); } }B题:给定一个字符串,构造一种方案使得他不为回文串,如果没有方案输出-1. 直接把字符串排序之后判断他还是不是回文串即可。
#include<stdio.h> #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; char ss[1005]; int len; void solve() { scanf("%s",&ss[1]); len = strlen(ss+1); sort(ss+1,ss+1+len); bool fl = 0; for(int i=1,j=len;i<=j;i++,j--) { if(ss[i]!=ss[j]) { fl = 1; break; } } if(!fl) puts("-1"); else { for(int i=1;i<=len;i++) putchar(ss[i]); puts(""); } } int main() { int T; scanf("%d",&T); while(T--) { solve(); } }C题:给定一个串B,长度为n/2,求一个串A,长度,使得满足Ai+An-i+1 == Bi,保证有解。 考虑每一个B,他对应的Ai限制条件分别由Ai-1和An-i+2限制,在考虑完他们的限制之后贪心Ai选最小,An-i+1选最大。
#include<stdio.h> #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef unsigned long long ll; const int maxn = 200005; int n; ll b[maxn]; ll a[maxn]; int main() { cin>>n; n/=2; ll mx,mi; cin>>b[1]; a[0] = 0; a[2*n]=b[1]; for(int i=2;i<=n;i++) { cin>>b[i]; ll oo; mx = min(a[2*n-i+2],b[i]); mi = max(a[i-1],b[i]-mx); a[i] = mi; a[2*n-i+1] = b[i]-a[i]; } for(int i=1;i<=2*n;i++) cout<<a[i]<<' '; }D题:给一张图,给每个点选择数字(1,2,3)其中一种,使得边的两边总为奇数的方案。 由于图可能不联通,所以对于每个子图跑一个二分图染色之后,乘法原理方案相乘。
#include<stdio.h> #include<cstdio> #include<algorithm> #include<iostream> #include<cstring> using namespace std; typedef unsigned long long ll; const int maxn = 200005; int n; ll b[maxn]; ll a[maxn]; int main() { cin>>n; n/=2; ll mx,mi; cin>>b[1]; a[0] = 0; a[2*n]=b[1]; for(int i=2;i<=n;i++) { cin>>b[i]; ll oo; mx = min(a[2*n-i+2],b[i]); mi = max(a[i-1],b[i]-mx); a[i] = mi; a[2*n-i+1] = b[i]-a[i]; } for(int i=1;i<=2*n;i++) cout<<a[i]<<' '; }E:给定两个1,,n的排列a,b,每次询问(la,ra) (lb,rb)即询问两个排列的两个区间中有多少个相同的数字,修改是交换两个b排列中的数。 离散化之后,问题转化为单点删除添加,矩形查询点数,树套树就可以了。不想写orz F题:给n1e5,len1e5,k100给定一个数字范围在[1,k]或者为-1的长度n数组,求把这个数组所有为-1的数改为[1,k]之间的数之后满足不存在长度大于等于len的区间,区间所有的数相同方案数%998244353。 一个十分巧妙的dp, F[i][j]表示当前考虑到数组第i位,第i位填j的方案数。那么f[i][j] = sum f[i][1-->k] - sum[i-len][1-->k] + dp[i-len][j],即直接在后面添一个j,并且减去前面长度len-1位都为j的方案数,再加上i-len位为j的方案数(因为前len-1位都为j,因为前面的方案都保证合法然而减去的方案中包含了i-len为j,那么就是减多了方案就加回来)。
#include<stdio.h> #include<iostream> #include<cstdio> #include<algorithm> using namespace std; const int maxn = 200005; const int mod = 998244353; int add(int x,int y) { x+=y; return x>=mod?x-mod:x; } int sumdp[maxn],dp[maxn][105]; int len,k,n,nm[maxn],cnt[105][maxn]; int main() { scanf("%d%d%d",&n,&k,&len); if(len==1) { puts("0"); return 0; } for(int i=1;i<=n;i++) { scanf("%d",&nm[i]); for(int j=1;j<=k;j++) { cnt[j][i] = cnt[j][i-1] + (nm[i]==-1||nm[i]==j); } } if(nm[1]==-1) { for(int j=1;j<=k;j++) dp[1][j] = 1; sumdp[1]=k; } else dp[1][nm[1]] = 1,sumdp[1]=1; for(int i=2;i<=n;i++) { if(nm[i]!=-1) { int j = nm[i]; dp[i][j] = sumdp[i-1]; if(i<len||cnt[j][i]-cnt[j][i-len]!=len) goto zz; if(i==len) dp[i][j] = add(dp[i][j],mod-1); else dp[i][j] = add(dp[i][j],add(mod-sumdp[i-len],dp[i-len][j])); } else { for(int j=1;j<=k;j++) { dp[i][j] = sumdp[i-1]; if(i<len||cnt[j][i]-cnt[j][i-len]!=len) continue; if(i==len) dp[i][j] = add(dp[i][j],mod-1); else dp[i][j] = add(dp[i][j],add(mod-sumdp[i-len],dp[i-len][j])); } } zz:for(int j=1;j<=k;j++) sumdp[i] = add(sumdp[i],dp[i][j]); } printf("%d",sumdp[n]); }G题:6s q , n 2e5 ai 1e6 k 5 给定一个序列,这个序列里的每一个元素由一个k元组构成,定义两个k元组之间的距离为这两个组里面对应的那一元的差的绝对值的和。 [latex]\sum \limits_{i = 1}^{k} |a_{x, i} - a_{y, i}| [/latex] 考虑转化原式子为(抄自官方题解): [latex] \sum \limits_{i = 1}^{k} |a_{x, i} - a_{y, i}| = \sum \limits_{i = 1}^{k} c_i (a_{x, i} - a_{y, i}) = \sum \limits_{i = 1}^{k} c_i a_{x, i} - \sum \limits_{i = 1}^{k} c_i a_{y, i}[/latex] where ci=1 if ax,i≥ay,i, otherwise ci=−1. 然后我们发现对于这样的一个式子,如果我们在原本的基础上修改取反c的符号,最终这个值一定是不会变大的,也就是说对于两个k元组他们最终的距离一定就是枚举c符号后最大的那个值。 所以做法就比较明了了,枚举c的符号,区间找到最大的 [latex] \sum \limits_{i = 1}^{k} c_i a_{x, i} [/latex] 和 [latex] \sum \limits_{i = 1}^{k} - c_i a_{y, i} [/latex] 即可,线段树之。
#include<cstdio> #include<algorithm> #include<iostream> #include<cmath> using namespace std; const int maxn = 400005; int n,k,S; int A[maxn][6]; struct node{ node *ls,*rs; int a[1<<5]; }z[maxn],*rt; int tot; void mktree(node *&p,int L,int R) { p = &z[++tot]; if(L==R) { for(int s=0;s<=S;s++) { for(int j=1;j<=k;j++) { if(s>>(j-1)&1) p->a[s] += A[L][j]; else p->a[s]-=A[L][j]; } } return; } int mid = (L+R)>>1; mktree(p->ls,L,mid); mktree(p->rs,mid+1,R); for(int s=0;s<=S;s++) { p->a[s] = max(p->ls->a[s],p->rs->a[s]); } } void change(node *&p,int L,int R,int x) { if(L==R) { for(int s=0;s<=S;s++) { p->a[s] = 0; for(int j=1;j<=k;j++) { if(s>>(j-1)&1) p->a[s] += A[L][j]; else p->a[s]-=A[L][j]; } } return; } int mid = (L+R)>>1; if(x<=mid) change(p->ls,L,mid,x); else change(p->rs,mid+1,R,x); for(int s=0;s<=S;s++) { p->a[s] = max(p->ls->a[s],p->rs->a[s]); } } int query(node *&p,int l,int r,int x,int y,int s) { if(x<=l&&r<=y) return p->a[s]; int mid = (l+r)>>1; if(x>mid) return query(p->rs,mid+1,r,x,y,s); else if(y<=mid) return query(p->ls,l,mid,x,y,s); else return max(query(p->ls,l,mid,x,y,s),query(p->rs,mid+1,r,x,y,s)); } int main() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { for(int j=1;j<=k;j++) { scanf("%d",&A[i][j]); } } S = (1<<k)-1; mktree(rt,1,n); int q; scanf("%d",&q); while(q--) { int op;scanf("%d",&op); if(op==1) { int x; scanf("%d",&x); for(int i=1;i<=k;i++) { scanf("%d",&A[x][i]); } change(rt,1,n,x); } else { int L,R; scanf("%d%d",&L,&R); int ans = 0; for(int i=0;i<(1<<(k-1));i++) { ans = max(ans,query(rt,1,n,L,R,i) + query(rt,1,n,L,R,S^i) ); } printf("%d\n",ans); } } }