11月15日 Educational Codeforces Round 56
题目链接:https://codeforces.com/contest/1093

D – Beautiful Graph

思路:
一个简单的计数题。

首先根据题意可以得知,一条边两端的奇偶性一定要是不同的,因此只有二分图是存在可行方案的。考虑计数,先二分图染色(假设染成黑白两色),那么对于每一个连通块可行的方案就是 $2^{白色点数}+2^{黑色点数}$,然后总方案就是每个连通块方案的乘积。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define MAXN 300005
#define p 998244353
#define INF 0x3f3f3f3f
#define rint register int
#define LL long long
#define LD long double
using namespace std;

int T, n, m, ans, num, tot, cnt, head[MAXN], col[MAXN], Pow[MAXN];

struct Edge {int next, to;} edge[MAXN*2];

void addedge(int from, int to)
{
    edge[++cnt].next=head[from];
    edge[cnt].to=to;
    head[from]=cnt;
}

int ksm(int x, int y)
{
    LL sum=1;
    while(y)
    {
        if(y&1) sum=1LL*sum*x%p;
        x=1LL*x*x%p; y>>=1;
    }
    return sum;
}

bool dfs(int x, int color)
{
    col[x]=color; tot++;
    if(color==0) num++;
    for(int i=head[x]; i; i=edge[i].next)
    {
        int to=edge[i].to;
        if(col[to]==-1)
        {
            if(!dfs(to, 1-color)) return false;
        }
        else if(col[to]==col[x]) return false;
    }
    return true;
}

void solve()
{
    cnt=0; ans=1;
    scanf("%d%d", &n, &m);
    for(rint i=1; i<=n; i++) head[i]=0, col[i]=-1;
    for(rint i=1; i<=m; ++i)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        addedge(x, y);
        addedge(y, x);
    }
    for(rint i=1; i<=n; ++i)
    {
        if(col[i]>=0) continue;
        tot=num=0;
        if(dfs(i, 0)) ans=1LL*ans*(Pow[num]+Pow[tot-num])%p;
        else {printf("0\n"); return ;}
    }
    printf("%d\n", ans);
}

int main()
{
    scanf("%d", &T);
    Pow[0]=1;
    for(rint i=1; i<=300000; ++i) Pow[i]=Pow[i-1]*2%p;
    while(T--) solve();
    return 0;
}

E -Intersection of Permutations

思路:
E 题就是个这么毒瘤的数据结构。。正解似乎是树套树,但是 CDQ 显然能做。

先可以将问题转化一下,处理出数组 $b$ 中的数字在数组 $a$ 中的位置记为数组 $c$,那么询问操作就是询问 $c[lb~rb]$ 中有多少个数的值在 $la~ra$ 内。这是一个二维数点问题,可以使用树状数组维护,由于有修改操作,那么就用 CDQ 基于时间一维进行分治就好了。时间复杂度也是 $O((n+m)log^2n)$。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define MAXN 200005
#define MAXM
#define INF 0x3f3f3f3f
#define rint register int
#define LL long long
#define LD long double
using namespace std;

int n, m, cnt, a[MAXN], b[MAXN], pos[MAXN], t[MAXN], ans[MAXN];

struct Act
{
    int id, x, y, val;
    Act(int a=0, int b=0, int c=0, int d=0)
    {
        id=a; x=b; y=c; val=d;
    }
} act[MAXN*5], temp[MAXN*5];

bool CMP(Act x, Act y)
{
    return x.x<y.x;
}

void add(int x, int y)
{
    for(; x<=200001; x+=(x&-x)) t[x]+=y;
}

int ask(int x)
{
    int sum=0;
    for(; x; x-=(x&-x)) sum+=t[x];
    return sum;
}

void cdq(int l, int r)
{
    if(l==r) return;
    int mid=(l+r)>>1;
    cdq(l, mid); cdq(mid+1, r);
    int L=l, R=mid+1;
    for(; R<=r; ++R)
    {
        if(!act[R].id) continue;
        while(act[L].x<=act[R].x && L<=mid)
        {
            if(!act[L].id) add(act[L].y, act[L].val);
            L++;
        }
        ans[act[R].id]+=act[R].val*ask(act[R].y);
    }
    for(int i=l; i<L; i++)
        if(!act[i].id) add(act[i].y, -act[i].val);
    merge(act+l, act+mid+1, act+mid+1, act+r+1, temp+l, CMP);
    for(int i=l; i<=r; i++) act[i]=temp[i];
}


int main()
{
    memset(ans, -1, sizeof(ans));
    scanf("%d%d", &n, &m);
    for(rint i=1; i<=n; ++i)
    {
        scanf("%d", &a[i]);
        pos[a[i]]=i;
    }
    for(rint i=1; i<=n; ++i)
    {
        scanf("%d", &b[i]);
        act[++cnt]=Act(0, i+1, pos[b[i]]+1, 1);
    }
    for(rint i=1; i<=m; ++i)
    {
        int op, la, ra, lb, rb, x, y;
        scanf("%d", &op);
        if(op==1)
        {
            ans[i]=0;
            scanf("%d%d%d%d", &la, &ra, &lb, &rb);
            act[++cnt]=Act(i, lb, la, 1);
            act[++cnt]=Act(i, lb, ra+1, -1);
            act[++cnt]=Act(i, rb+1, la, -1);
            act[++cnt]=Act(i, rb+1, ra+1, 1);
        }
        else
        {
            scanf("%d%d", &x, &y);
            act[++cnt]=Act(0, x+1, pos[b[x]]+1, -1);
            act[++cnt]=Act(0, y+1, pos[b[y]]+1, -1);
            swap(b[x], b[y]);
            act[++cnt]=Act(0, x+1, pos[b[x]]+1, 1);
            act[++cnt]=Act(0, y+1, pos[b[y]]+1, 1);
        }
    }
    cdq(1, cnt);
    for(int i=1; i<=m; i++)
        if(ans[i]!=-1) printf("%d\n", ans[i]);
}

F – Vasya and Array

思路:
是一个很有趣的 DP。

如果不考虑限制可以得到转移:$f[i][j]=\sum_{x=1}^{k} f[i-1][x]$,其中如果 $a[i]=-1$ 那么 $j$ 可以取 $1~k$ 所有数,否则 $j$ 只能取 j。接下来就是要把不合法的状态减去,我们记录一个数组 $pre[i]$ 表示从下标 $pre[i]$ 开始全可以取数字 $i$,由此我们就可以判断一个状态是否合法。如果数字 $j$ 在位置 $i$ 上不合法,那么我们就应该减去 $\sum_{x!=j} f[i-len][x]$。

代码:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define MAXN 100005
#define p 998244353
#define INF 0x3f3f3f3f
#define rint register int
#define LL long long
#define LD long double
using namespace std;

int n, k, len, ans, a[MAXN], f[MAXN][105], pre[105], sum[MAXN];

void add(int &x, int y)
{
    x=(x+y>=p)?x+y-p:x+y;
}

int main()
{
    scanf("%d%d%d", &n, &k, &len);
    for(rint i=1; i<=n; ++i) scanf("%d", &a[i]);
    memset(pre, -1, sizeof(pre));
    sum[0]=1;
    for(rint i=1; i<=n; ++i)
    {
        if(a[i]!=-1)
        {
            f[i][a[i]]=(i==1)?1:sum[i-1];
            for(rint j=1; j<=k; ++j)
            {
                if(j!=a[i]) pre[j]=-1;
                else if(pre[j]==-1) pre[j]=i;
            }
            if(pre[a[i]]!=-1 && i-pre[a[i]]+1>=len)
                add(f[i][a[i]], p-sum[i-len]), add(f[i][a[i]], f[i-len][a[i]]);
                //f[i][a[i]]=(f[i][a[i]]-sum[i-len]+f[i-len][a[i]]+p)%p;
        }
        else
        {
            for(rint j=1; j<=k; ++j)
            {
                if(pre[j]==-1) pre[j]=i;
                f[i][j]=(i==1)?1:sum[i-1];
            }

            for(rint j=1; j<=k; ++j)
                if(i-pre[j]+1>=len) add(f[i][j], p-sum[i-len]), add(f[i][j], f[i-len][j]);
                    //f[i][j]=(f[i][j]-sum[i-len]+f[i-len][j]+p)%p;
        }
        for(rint j=1; j<=k; j++) add(sum[i], f[i][j]);
            //sum[i]=(sum[i]+f[i][j])%p;
    }
    printf("%d\n", sum[n]);
}

G – Multidimensional Queries

思路:
G 题与 E 题比反而是一个比较简单的数据结构题。

考虑 $k$ 比较小,于是我们可以对一个点枚举在每一维上对答案的贡献情况,即在公式 $\sum \limits_{i = 1}^{k} |a_{x, i} – a_{y, i}|$ 去掉绝对值时的符号。对于我们枚举的每一种情况,由于去掉了绝对值,答案便是求一个区间内最大值与最小值之差,这个可以很简单的用线段树去维护。

为了减小码量,对于每一棵线段树可以只维护最小值。而最大值与最小值之差就是当前状态下的最小值与相反状态下最小值的和。

代码:

Copy
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#include <set>
#define MAXN 200005
#define ls (root<<1)
#define rs (root<<1|1)
#define mid ((l+r)>>1)
#define INF 0x3f3f3f3f
#define rint register int
#define LL long long
#define LD long double
using namespace std;

int n, k, q, w[6], sum[32];

struct Segtree
{
    int val[MAXN*4], num[MAXN];

    void up(int root)
    {
        val[root]=min(val[ls], val[rs]);
    }

    void build(int root, int l, int r)
    {
        if(l==r)
        {
            val[root]=num[l];
            return;
        }
        build(ls, l, mid);
        build(rs, mid+1, r);
        up(root);
    }

    void update(int root, int l, int r, int x, int k)
    {
        if(l>x || r<x) return;
        if(l==r)
        {
            val[root]=k;
            return;
        }
        update(ls, l, mid, x, k);
        update(rs, mid+1, r, x, k);
        up(root);
    }

    int query(int root, int l, int r, int x, int y)
    {
        if(l>y || r<x) return INF;
        if(l>=x && r<=y) return val[root];
        return min(query(ls, l, mid, x, y), query(rs, mid+1, r, x, y));
    }
} t[32];


int main()
{
    scanf("%d%d", &n, &k);
    for(rint i=1; i<=n; ++i)
    {
        for(rint j=0; j<k; ++j) scanf("%d", &w[j]);
        for(rint sta=0; sta<(1<<k); ++sta)
            for(rint j=0; j<k; ++j)
            {
                if((sta>>j)&1) t[sta].num[i]+=w[j];
                else t[sta].num[i]-=w[j];
            }
    }
    for(rint sta=0; sta<(1<<k); ++sta) t[sta].build(1, 1, n);
    scanf("%d", &q);
    while(q--)
    {
        int op, x, y, ans=0;
        scanf("%d", &op);
        if(op==2)
        {
            scanf("%d%d", &x, &y);
            for(rint sta=0; sta<(1<<k); ++sta)
                sum[sta]=t[sta].query(1, 1, n, x, y);
            for(rint sta=0; sta<(1<<k); ++sta)
                ans=max(ans, -(sum[sta]+sum[(1<<k)-1-sta]));
            printf("%d\n", ans);
        }
        else
        {
            scanf("%d", &x);
            for(rint i=0; i<k; ++i) scanf("%d", &w[i]);
            for(rint sta=0; sta<(1<<k); ++sta)
            {
                int temp=0;
                for(rint i=0; i<k; ++i)
                {
                    if((sta>>i)&1) temp+=w[i];
                    else temp-=w[i];
                }
                t[sta].update(1, 1, n, x, temp);
            }
        }
    }
}

发表评论

邮箱地址不会被公开。 必填项已用*标注