Educational Codeforces Round 56 Editorial

A.Dice Rolling

题意:Mishka 有一个六面的骰子,每面分别为 2 ~ 7,而且 Mishka 是欧皇,可以控制自己每次掷到的数字。Mishka 现在想掷若干次骰子,使得掷到的点数总和为 x,请求出任意一种掷骰子的方案,并输出掷骰子的次数。由于 Mishka 很好奇不同数字的方案,所以有 t 组询问。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
int a[maxn],n,t,x;
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        scanf("%d",&x);
        if(x%2==0)
        {
            printf("%d\n",x/2);
        }
        else
        {
            printf("%d\n",x/2);
        }
    }
} 

B.Letters Rearrangin

题意:t 组询问,每次给你一个字符串,将其重新排列使其成为一个非回文串,如果无解则输出 -1 。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
int a[1010],n,t,x,flag;
char s[1010];
int main()
{
    scanf("%d",&t);
    for(int i=1;i<=t;i++)
    {
        memset(a,0,sizeof(a));
        flag=0;
        scanf("%s",&s);
        int l=strlen(s);
        for(int i=0;i<l;i++)
        {
            if(a[s[i]]==0) flag++;
            a[s[i]]++;
        }
        if(flag==1) {
            printf("-1\n");
            continue;
        }
        for(int i=1;i<=128;i++)
        {
            for(int j=1;j<=a[i];j++)
               printf("%c",i);
        }
        printf("\n");
    }
} 

C.Mishka and the Last Exam

题意:有一个长度为 n(n 为偶数)的数列 a1..n?,ai<=ai+1?,bi=ai+an-i+1, 现在告诉你 n 和 b1..n/2??,求a1..n? 。

思路:采用贪心的策略,对于任意一个bi,让a n-i+1尽可能大,ai尽能小,所以从中间开始贪心。(注意long long)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
long long a[maxn],b[maxn];
int n,t,x,flag,l,r;
char s[1010];
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n/2;i++) scanf("%I64d",&b[i]);
    l=n/2;r=n/2+1;
    a[l]=a[r]=b[l]/2;
    a[r]+=b[l]%2;
    for(int i=n/2-1;i>=1;i--)
    {
        if(b[i]-a[l]<a[r])
        {
            a[r+1]=a[r];
            a[l-1]=b[i]-a[r+1];
        }
        else{
            a[l-1]=a[l];
            a[r+1]=b[i]-a[l-1];
        }
        l--;r++;
    }
    for(int i=1;i<=n;i++) printf("%I64d ",a[i]);
} 

D.Beautiful Graph

题意:给你一个 n 个点 m 条边的无向图。你需要给每个点一个点权,使得每条边连接的两个点点权奇偶不同。点权的值域为 {1,2,3}。请求出方案数对 998244353 取模的结果。图中没有重边或自环。

思路:要做一次 bfsbfs 染色并判断是否能完成染色即可bfs染色,加个快速幂即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int p=998244353;
const int maxn=6e5+10;
const int maxm=6e5+10;
int x,y,z,l=0,t,n,m;
int link[maxm],w[maxm],ne[maxm],first[maxn],fa[maxn];
int f[maxn];
int ans,flag,ana;
long long an=1;
LL quick_mod(LL a,LL b)
{
    LL ans=1;
    a%=p;
    while(b)
      {
          if(b&1) {ans=ans*a%p;b--;}
          b>>=1;a=a*a%p;
      }
    return ans;
}
void add(int x,int y)
{
    link[++l]=y;ne[l]=first[x];first[x]=l;
}
int dfs(int x,int fa,bool col)
{
    f[x]=col;ana++;if(col==0)ans++;
    //printf("%d %d\n",x,fa);
    for(int i=first[x];i;i=ne[i])
    {
        if(link[i]==fa) continue;
        if(f[link[i]]==-1)
        {
            if (dfs(link[i],x,!col)) ;
            else return 0;
        }
        else
        {
           if(f[link[i]]!=(!col)) return 0;
        }
    }
    return 1;
}
int getfa(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=getfa(fa[x]);
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        an=1;
        for(int i=1;i<=n;i++) f[i]=-1,first[i]=0,fa[i]=i;
        l=0;
        for(int i=1;i<=m;i++)
          {
          scanf("%d%d",&x,&y);
          int fx=getfa(x),fy=getfa(y);
          fa[fx]=fy;
          add(x,y);add(y,x);
          }
        flag=1;
        for(int i=1;i<=n;i++)
        {
            ans=0;ana=0;
            if(fa[i]==i)
            {
                if(dfs(i,0,0))
                {
                    //printf("%d %d %d\n",i,ana,ans);
                    an=an*(quick_mod(2,ans)+quick_mod(2,ana-ans))%p;
                }
                else
                {
                flag=0;
                break;
                }
            }
        }
        if(!flag) printf("0\n");
        else printf("%I64d\n",an);
    }
} 

E.Intersection of Permutations

题意:给你长度为n的两个序列,有m个询问,1 la ra lb rb 表示查询a数组[la,ra]区间内和b数组[lb,rb]区间内相同的数的个数

思路1:用 pa[i]表示 i这个数在第一个排列中出现的位置,pb[i]表示 i这个数在第二个排列中出现的位置,那么容易发现问题变成了二维数点问题,cdq 分治离线统计答案即可

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,ta[N],tb[N],pa[N],pb[N],ans[N],tot,f[N],cnt;
struct node{
    bool fx;int x,y,k,id,tim;
    node(int f=0,int x=0,int y=0,int k=0,int i=0,int t=0):
        fx(f),x(x),y(y),k(k),id(i),tim(t) {}
    bool operator< (const node &b) const
    {return x<b.x||(x==b.x&&tim<b.tim);}
}a[(N<<2)+N],b[(N<<2)+N];
void add(int x,int k){for(;x<=n;x+=(x&-x))f[x]+=k;}
int query(int x){int ret=0;for(;x;x-=(x&-x))ret+=f[x];return ret;}
void solve(int l,int r)
{
    if(l>=r) return;
    int mid=(l+r)>>1,p0=l,p1=mid+1;
    for(int i=l;i<=r;i++)
        if(!a[i].fx&&a[i].tim<=mid) add(a[i].y,a[i].k);
        else if(a[i].fx&&a[i].tim>mid) ans[a[i].id]+=a[i].k*query(a[i].y);
    for(int i=l;i<=r;i++)
        if(!a[i].fx&&a[i].tim<=mid) add(a[i].y,-a[i].k);

    for(int i=l;i<=r;i++)
        if(a[i].tim<=mid) b[p0++]=a[i];
        else b[p1++]=a[i];

    for(int i=l;i<=r;i++) a[i]=b[i];
    solve(l,mid);solve(mid+1,r);
}
int main()
{
    scanf("%d",&n);scanf("%d",&m);
    for(int i=1;i<=n;i++) scanf("%d",&ta[i]),pa[ta[i]]=i;
    for(int i=1;i<=n;i++) scanf("%d",&tb[i]),pb[tb[i]]=i;
    for(int i=1;i<=n;i++)
        a[++cnt]=node(0,pa[i],pb[i],1,0,cnt);
    for(int i=1,op,x1,y1,x2,y2,w1,w2;i<=m;i++)
    {
        scanf("%d",&op);
        if(op==1)
        {
            tot++;
            scanf("%d",&x1);scanf("%d",&x2);
            scanf("%d",&y1);scanf("%d",&y2);
            x1--;y1--;
            a[++cnt]=node(1,x2,y2,1,tot,cnt);
            a[++cnt]=node(1,x1,y2,-1,tot,cnt);
            a[++cnt]=node(1,x2,y1,-1,tot,cnt);
            a[++cnt]=node(1,x1,y1,1,tot,cnt);
        }
        else{
            scanf("%d",&w1);scanf("%d",&w2);
            y1=w1;x1=pa[tb[w1]];y2=w2;x2=pa[tb[w2]];
            swap(tb[w1],tb[w2]);
            a[++cnt]=node(0,x2,y2,-1,0,cnt);
            a[++cnt]=node(0,x1,y1,-1,0,cnt);
            a[++cnt]=node(0,x2,y1,1,0,cnt);
            a[++cnt]=node(0,x1,y2,1,0,cnt);
        }
    }
    sort(a+1,a+1+cnt);
    solve(1,cnt);
    for(int i=1;i<=tot;i++) printf("%d\n",ans[i]);
    return 0;
}

思路2:时间线段树+扫描线

上一棵时间线段树。将一个点覆盖在它出现的时间区间内,一个询问则在从单独代表这个询问的时间的线段树节点到线段树的根的路径上都放一个,遍历线段树的时候,

对于每个节点上放的询问们和点们,都做一遍扫描线。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=‘ ‘;
    while(ch<‘0‘||ch>‘9‘) ch=getchar();
    while(ch>=‘0‘&&ch<=‘9‘) q=q*10+ch-‘0‘,ch=getchar();
    return q;
}
const int N=200005;
int n,m,qcnt;
int a[N],X[N],T[N],ans[N],s[N];
struct node{int x,Y1,Y2,id;};
vector<node> tr[N<<2];

void insq(int x,int s,int t,int i,node k) {//将询问放在时间线段树上
    tr[i].push_back(k); if(s==t) return;
    int mid=(s+t)>>1;
    if(x<=mid) insq(x,s,mid,i<<1,k);
    else insq(x,mid+1,t,(i<<1)|1,k);
}
void insp(int l,int r,int s,int t,int i,node k) {//将点放在时间线段树上
    if(l<=s&&t<=r) {tr[i].push_back(k);return;}
    int mid=(s+t)>>1;
    if(l<=mid) insp(l,r,s,mid,i<<1,k);
    if(mid+1<=r) insp(l,r,mid+1,t,(i<<1)|1,k);
}

bool cmp(node A,node B) {return A.x==B.x?A.id==0:A.x<B.x;}
int lowbit(int x) {return x&(-x);}
void add(int x,int v) {while(x<=n) s[x]+=v,x+=lowbit(x);}
int query(int x) {int re=0; while(x) re+=s[x],x-=lowbit(x); return re;}
void work(int s,int t,int i) {
    sort(tr[i].begin(),tr[i].end(),cmp);//扫描线
    int sz=tr[i].size();
    for(RI j=0;j<sz;++j) {
        if(!tr[i][j].id) add(tr[i][j].Y1,1);
        else {
            int kl=query(tr[i][j].Y2)-query(tr[i][j].Y1-1);
            if(tr[i][j].id<0) ans[-tr[i][j].id]-=kl;
            else ans[tr[i][j].id]+=kl;
        }
    }
    for(RI j=0;j<sz;++j) if(!tr[i][j].id) add(tr[i][j].Y1,-1);
    if(s==t) return;
    int mid=(s+t)>>1;
    work(s,mid,i<<1),work(mid+1,t,(i<<1)|1);
}

int main()
{
    int op,X1,Y1,X2,Y2;
    n=read(),m=read();
    for(RI i=1;i<=n;++i) a[read()]=i;
    for(RI i=1;i<=n;++i) X[i]=a[read()],T[i]=0;
    for(RI i=1;i<=m;++i) {
        op=read();
        if(op==1) {
            X1=read(),X2=read(),Y1=read(),Y2=read(),++qcnt;
            insq(i,0,m,1,(node){X1-1,Y1,Y2,-qcnt});
            insq(i,0,m,1,(node){X2,Y1,Y2,qcnt});
        }
        else {
            X1=read(),X2=read();
            insp(T[X1],i-1,0,m,1,(node){X[X1],X1,0,0});
            insp(T[X2],i-1,0,m,1,(node){X[X2],X2,0,0});
            swap(X[X1],X[X2]),T[X1]=T[X2]=i;
        }
    }
    for(RI i=1;i<=n;++i) insp(T[i],m,0,m,1,(node){X[i],i,0,0});
    work(0,m,1);
    for(RI i=1;i<=qcnt;++i) printf("%d\n",ans[i]);
    return 0;
}

F. Vasya and Array

题意:给你一个长度为n的序列,一个正整数K,和长度len,序列中的数都是1-k或者为-1,-1表示可以填任何数。让你在-1的地方填数,使得没有长度?len的相等数字。

思路:DP,DP[i][j]表示填到第i为的数字为j的方案数,S[i]为DP[i][j](1<=j<=k)注意转移

#include <bits/stdc++.h>
#define md 998244353
#define maxn 100001
#define max(a,b) (a>b?a:b)
#define maxk 101
int n,k,len,a[maxn];
int f[maxn][maxk],s[maxn],cnt[maxn][maxk];
void inc(int &a,int b){a=((a+b>=md)?a+b-md:a+b);}
int main(){
    scanf("%d%d%d",&n,&k,&len);
    for (register int i=1;i<=n;++i)
        scanf("%d",&a[i]);
    for (register int i=1;i<=n;++i)
        for (register int j=1;j<=k;++j)
            inc(cnt[i][j],cnt[i-1][j]+(a[i]==j||a[i]==-1));
    for (register int i=1;i<=n;++i){
        for (register int j=1;j<=k;++j){
            if (!(a[i]==j||a[i]==-1)) continue;
            int add=1;
            if (i>1) add=s[i-1];
            inc(f[i][j],add);
            bool ok=i>=len;
            int l=max(1,i-len+1);
            ok&=(cnt[i][j]-cnt[l-1][j]==len);
            if (!ok) continue;
            if (i==len) {inc(f[i][j],md-1);continue;}
            int sum=f[i-len][j];
            inc(sum,md-s[i-len]);
            inc(f[i][j],sum);
        }
        for (register int j=1;j<=k;++j)
            inc(s[i],f[i][j]);
    }
    printf("%d\n",s[n]);
}

G.Multidimensional Queries

题意:给你 n 个 k 维的点 ,求区间内两个点曼哈顿距离的最大值。

思路:习惯性的把曼哈顿距离的绝对值拆出来,用二进制表示31 的二进制表示是 11111,表示 5维的一个点的坐标加入的正负情况都为正(即 x[1] - y[1] + x[2] - y[2] + x[3] - y[3] + x[4] - y[4] + x[5] - y[5]

用线段树维护

#include <bits/stdc++.h>
#define Fast_cin ios::sync_with_stdio(false), cin.tie();
#define rep(i, a, b) for(register int i = a; i <= b; i++)
#define per(i, a, b) for(register int i = a; i >= b; i--)
#define DEBUG(x) cerr << "DEBUG" << x << " >>> " << endl;
using namespace std;

typedef unsigned long long ull;
typedef long long ll;

template <typename _T>
inline void read(_T &f) {
    f = 0; _T fu = 1; char c = getchar();
    while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) fu = -1; c = getchar(); }
    while(c >= ‘0‘ && c <= ‘9‘) { f = (f << 3) + (f << 1) + (c & 15); c = getchar(); }
    f *= fu;
}

template <typename T>
void print(T x) {
    if(x < 0) putchar(‘-‘), x = -x;
    if(x < 10) putchar(x + 48);
    else print(x / 10), putchar(x % 10 + 48);
}

template <typename T>
void print(T x, char t) {
   print(x); putchar(t);
}

const int N = 2e5 + 5;

struct ele { int f[32]; };

struct Node {
    int l, r; ele val;
}p[N << 2];

int t[N][5];
int n, m, k;

ele merge(ele a, ele b) {
    for(register int i = 0; i < (1 << k); i++) a.f[i] = max(a.f[i], b.f[i]);
    return a;
}

void build(int u, int l, int r) {
    p[u].l = l; p[u].r = r;
    if(l == r) {
        for(register int i = 0; i < (1 << k); i++) {
            p[u].val.f[i] = 0;
            for(register int j = 0; j < k; j++) {
                if(i & (1 << j)) p[u].val.f[i] += t[l][j];
                else p[u].val.f[i] -= t[l][j];
            }
        }
        return;
    }
    int mid = (l + r) >> 1;
    build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
    p[u].val = merge(p[u << 1].val, p[u << 1 | 1].val);
}

void change(int u, int l) {
    if(p[u].l == p[u].r) {
        for(register int i = 0; i < (1 << k); i++) {
            p[u].val.f[i] = 0;
            for(register int j = 0; j < k; j++) {
                if(i & (1 << j)) p[u].val.f[i] += t[l][j];
                else p[u].val.f[i] -= t[l][j];
            }
        }
        return;
    }
    int mid = (p[u].l + p[u].r) >> 1;
    if(mid >= l) change(u << 1, l); else change(u << 1 | 1, l);
    p[u].val = merge(p[u << 1].val, p[u << 1 | 1].val);
}

ele query(int u, int l, int r) {
    if(p[u].l >= l && p[u].r <= r) return p[u].val;
    int mid = (p[u].l + p[u].r) >> 1;
    if(mid >= l && mid + 1 <= r) return merge(query(u << 1, l, r), query(u << 1 | 1, l, r));
    else if(mid >= l) return query(u << 1, l, r); return query(u << 1 | 1, l, r);
}

int main() {
    read(n); read(k);
    for(register int i = 1; i <= n; i++) {
        for(register int j = 0; j < k; j++) {
            read(t[i][j]);
        }
    }
    build(1, 1, n); read(m);
    while(m--) {
        int opt; read(opt);
        if(opt == 1) {
            int i; read(i);
//          cout << i << " " << k << endl;
            for(register int j = 0; j < k; j++) read(t[i][j]);
            change(1, i);
        }
        if(opt == 2) {
            int l, r; read(l); read(r);
            ele res = query(1, l, r); int ans = 0;
            for(register int i = 0; i < (1 << (k - 1)); i++) ans = max(ans, res.f[i] + res.f[(1 << k) - 1 - i]);
            print(ans, ‘\n‘);
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/The-Pines-of-Star/p/10340954.html

时间: 2024-10-22 02:53:18

Educational Codeforces Round 56 Editorial的相关文章

Multidimensional Queries(二进制枚举+线段树+Educational Codeforces Round 56 (Rated for Div. 2))

题目链接: https://codeforces.com/contest/1093/problem/G 题目: 题意: 在k维空间中有n个点,每次给你两种操作,一种是将某一个点的坐标改为另一个坐标,一种操作是查询[l,r]中曼哈顿距离最大的两个点的最大曼哈顿距离. 思路: 对于曼哈顿距离,我们将其绝对值去掉会发现如下规律(以二维为例): 故这题我们可以用线段树来维护[l,r]中上述每种情况的最大值和最小值,用二进制来枚举xy的符号(1为正,0为负),最后答案是 每种情况中区间最大值-区间最小值

Educational Codeforces Round 56 (Rated for Div. 2) ABCD

题目链接:https://codeforces.com/contest/1093 A. Dice Rolling 题意: 有一个号数为2-7的骰子,现在有一个人他想扔到几就能扔到几,现在问需要扔多少次,能使扔出的总和等于xi. 题解: 由于是special judge,模拟一下搞搞就行了= = 代码如下: #include <bits/stdc++.h> using namespace std; int main(){ int t; cin>>t; int n; while(t--

Educational Codeforces Round 56 (Rated for Div. 2)

涨rating啦.. 不过话说为什么有这么多数据结构题啊,难道是中国人出的? A - Dice Rolling 傻逼题,可以用一个三加一堆二或者用一堆二,那就直接.. #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> #include<map> #include<vector> #include<c

Educational Codeforces Round 73 (Rated for Div. 2)

比赛链接:Educational Codeforces Round 73 (Rated for Div. 2) 官方题解:Educational Codeforces Round 73 Editorial A. 2048 Game 题意 如果一个只包含 \(2\) 的幂次的集合,问能否从中选择一些数使得和为 \(2048\). 思路 不断合并直到凑到 \(2048\). 代码 #include <bits/stdc++.h> using namespace std; int main() {

Educational Codeforces Round 21 G. Anthem of Berland(dp+kmp)

题目链接:Educational Codeforces Round 21 G. Anthem of Berland 题意: 给你两个字符串,第一个字符串包含问号,问号可以变成任意字符串. 问你第一个字符串最多包含多少个第二个字符串. 题解: 考虑dp[i][j],表示当前考虑到第一个串的第i位,已经匹配到第二个字符串的第j位. 这样的话复杂度为26*n*m*O(fail). fail可以用kmp进行预处理,将26个字母全部处理出来,这样复杂度就变成了26*n*m. 状态转移看代码(就是一个kmp

Educational Codeforces Round 23 F. MEX Queries(线段树)

题目链接:Educational Codeforces Round 23 F. MEX Queries 题意: 一共有n个操作. 1.  将[l,r]区间的数标记为1. 2.  将[l,r]区间的数标记为0. 3.  将[l,r]区间取反. 对每个操作,输出标记为0的最小正整数. 题解: hash后,用线段树xjb标记一下就行了. 1 #include<bits/stdc++.h> 2 #define ls l,m,rt<<1 3 #define rs m+1,r,rt<&l

Educational Codeforces Round 21 F. Card Game(网络流之最大点权独立集)

题目链接:Educational Codeforces Round 21 F. Card Game 题意: 有n个卡片,每个卡片有三个值:p,c,l; 现在让你找一个最小的L,使得满足选出来的卡片l<=L,并且所有卡片的p的和不小于k. 选择卡片时有限制,任意两张卡片的c之和不能为质数. 题解: 和hdu 1565 方格取数(2)一样,都是求最大点权独立集. 不难看出来,这题再多一个二分. 注意的是在构造二部图的时候,按照c值的奇偶性构造. 当c==1时要单独处理,因为如果有多个c==1的卡片,

Educational Codeforces Round 36 (Rated for Div. 2)

Educational Codeforces Round 36 (Rated for Div. 2) F. Imbalance Value of a Tree You are given a tree T consisting of n vertices. A number is written on each vertex; the number written on vertex i is ai. Let's denote the function I(x,?y) as the differ

Educational Codeforces Round 69 (Rated for Div. 2) B - Pillars

Educational Codeforces Round 69 (Rated for Div. 2) B - Pillars There are n pillars aligned in a row and numbered from 1 to n. Initially each pillar contains exactly one disk. The i-th pillar contains a disk having radius ai. You can move these disks