【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);
        }
    }
}
 
posted @ 2018-12-16 00:13  Newuser233  阅读(4)  评论(0)    收藏  举报