Codeforces Round #556 (Div. 1)

Codeforces Round #556 (Div. 1)

A. Prefix Sum Primes

給你一堆1,2,你能夠任意排序,要求你輸出的數列的前綴和中質數個數最大。ios

發現只有\(2\)是偶質數,那麼咱們先放一個\(2\),再放一個\(1\),接下來把\(2\)所有放掉再把\(1\)所有放掉就好了。promise

#include<iostream>
#include<cstdio>
using namespace std;
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,a[3];
int main()
{
    n=read();
    for(int i=1;i<=n;++i)a[read()]+=1;
    if(a[2])
    {
        printf("2 "),a[2]-=1;
        if(a[1])printf("1 "),a[1]-=1;
    }
    while(a[2])printf("2 "),a[2]-=1;
    while(a[1])printf("1 "),a[1]-=1;
    puts("");
    return 0;
}

B. Three Religions

給你一個串\(S\),會動態的修改三個串,修改是在三個串的末尾刪去或加入一個字符。
每次修改完以後回答這三個串可否表示成\(S\)的三個不交的子序列。函數

考慮一個\(dp\)\(f[i][a][b][c]\),表示當前考慮到了串的第\(i\)個位置,匹配了到了三個串的\(a,b,c\)位置。
而後發現第一維這個東西很是蠢。把狀態改一下,變成\(f[a][b][c]\)表示三個串分別匹配到\(a,b,c\)\(i\)的最小值。
這樣子單次修改轉移的複雜度就是\(O(len^2)\),這樣子複雜度就很對了。spa

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 100100
int n,Q;
int nxt[MAX][26],lst[26];
char s[MAX],a[4][MAX];
int len[4],f[255][255][255];
void dp(int x,int y,int z)
{
    if(!(x|y|z))return;f[x][y][z]=n+1;
    if(x&&f[x-1][y][z]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x-1][y][z]][a[1][x]-97]);
    if(y&&f[x][y-1][z]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x][y-1][z]][a[2][y]-97]);
    if(z&&f[x][y][z-1]<=n)f[x][y][z]=min(f[x][y][z],nxt[f[x][y][z-1]][a[3][z]-97]);
}
int main()
{
    scanf("%d%d%s",&n,&Q,s+1);
    for(int i=0;i<26;++i)lst[i]=n+1;
    for(int i=n;~i;--i)
    {
        for(int j=0;j<26;++j)nxt[i][j]=lst[j];
        if(i)lst[s[i]-97]=i;
    }
    while(Q--)
    {
        char ch[2],ss[2];int x;
        scanf("%s%d",ch,&x);
        if(ch[0]=='+')
        {
            scanf("%s",ss);
            a[x][++len[x]]=ss[0];
            if(x==1)
                for(int i=0;i<=len[2];++i)
                    for(int j=0;j<=len[3];++j)
                        dp(len[1],i,j);
            if(x==2)
                for(int i=0;i<=len[1];++i)
                    for(int j=0;j<=len[3];++j)
                        dp(i,len[2],j);
            if(x==3)
                for(int i=0;i<=len[1];++i)
                    for(int j=0;j<=len[2];++j)
                        dp(i,j,len[3]);
        }
        else --len[x];
        if(f[len[1]][len[2]][len[3]]<=n)puts("YES");else puts("NO");
    }
    return 0;
}

C. Tree Generator™

給你一個括號序列,顯然一個合法的括號序列和一棵樹是對應的。
如今每次交換括號序列的兩個位置,求交換完以後的每一棵樹的直徑。code

首先問題能夠變成給定把(當作\(1\),把)當作\(-1\)
因而問題就變成了你要在括號序列上找到任意一段連續的子串,而且把它分紅兩段,使得後一半的值減去前一半的值最大。
而後線段樹維護一下就好了。排序

#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 200200
#define lson (now<<1)
#define rson (now<<1|1)
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
int n,Q;char s[MAX];
struct Node{int pre[2],suf[2],ans,sum,tot;}t[MAX<<2],L,R;
Node operator+(Node a,Node b)
{
    Node c;
    c.pre[0]=max(a.pre[0],a.sum+b.pre[0]);
    c.pre[1]=max(a.pre[1],max(a.tot+b.pre[0],-a.sum+b.pre[1]));
    c.suf[0]=max(b.suf[0],-b.sum+a.suf[0]);
    c.suf[1]=max(b.suf[1],max(b.tot+a.suf[0],b.sum+a.suf[1]));
    c.sum=a.sum+b.sum;
    c.tot=max(a.tot+b.sum,-a.sum+b.tot);
    c.ans=max(max(a.ans,b.ans),max(a.suf[0]+b.pre[1],a.suf[1]+b.pre[0]));
    return c;
}
void Modify(int now,int l,int r,int p)
{
    if(l==r){t[now]=(s[p]=='(')?L:R;return;}
    int mid=(l+r)>>1;
    if(p<=mid)Modify(lson,l,mid,p);
    else Modify(rson,mid+1,r,p);
    t[now]=t[lson]+t[rson];
}
int main()
{
    L=(Node){1,1,0,1,1,1,1};R=(Node){0,1,1,1,1,-1,1};
    n=(read()-1)<<1;Q=read();scanf("%s",s+1);
    for(int i=1;i<=n;++i)Modify(1,1,n,i);
    printf("%d\n",t[1].ans);
    while(Q--)
    {
        int x,y;swap(s[x=read()],s[y=read()]);
        Modify(1,1,n,x);Modify(1,1,n,y);
        printf("%d\n",t[1].ans);
    }
    return 0;
}

D. Abandoning Roads

你有一張圖,你須要對於每個點回答在任意一棵最小生成樹中,\(1\)號點到這個點的路徑的最小值是多少。
邊權只有兩種。three

首先小的邊權叫作\(a\),大的邊權叫作\(b\)
如今是\(a\)邊構成了一堆聯通塊,而後你用\(b\)邊把這些聯通塊連接而後求最短路。
這裏直接求最短路有一個問題就是你不能在同一個聯通塊內用\(b\)邊,也不能讓一條路徑重複的通過兩次同一個聯通塊。
那麼咱們考慮狀壓,記錄已經訪問過了哪一些聯通塊。
然而這樣子是\(2^n\)的。
發現大小爲\(1,2,3\)的聯通塊你不可能用\(b\)邊繞一圈再從新進來,因此只須要考慮大小至少爲\(4\)的聯通塊。這樣子複雜度就降下來了get

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
#define MAX 72
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
struct Line{int v,next,w;}e[500];
int h[MAX],cnt=1;
inline void Add(int u,int v,int w){e[cnt]=(Line){v,h[u],w};h[u]=cnt++;}
int f[MAX],sz[MAX];
int getf(int x){return x==f[x]?x:f[x]=getf(f[x]);}
void Merge(int x,int y){x=getf(x);y=getf(y);f[y]=x;sz[x]+=sz[y];}
int dis[MAX][131072];bool vis[MAX][131072];
struct Node{int u,d,S;};
bool operator<(Node a,Node b){return a.d>b.d;}
priority_queue<Node> Q;
void upd(int u,int d,int S){if(dis[u][S]>d)dis[u][S]=d,Q.push((Node){u,d,S});}
int n,m,A,B,book[MAX],tim;
int main()
{
    n=read();m=read();A=read();B=read();
    for(int i=1;i<=n;++i)f[i]=i,sz[i]=1,book[i]=-1;
    for(int i=1;i<=m;++i)
    {
        int u=read(),v=read(),w=read();
        Add(u,v,w);Add(v,u,w);
        if(w==A)Merge(u,v);
    }
    for(int i=1;i<=n;++i)
        if(book[i]==-1&&sz[getf(i)]>3)
        {
            for(int j=i;j<=n;++j)
                if(getf(i)==getf(j))
                    book[j]=tim;
            ++tim;
        }
    memset(dis,63,sizeof(dis));
    upd(1,0,(~book[1])?1<<book[1]:0);
    while(!Q.empty())
    {
        int u=Q.top().u,S=Q.top().S;Q.pop();
        if(vis[u][S])continue;vis[u][S]=true;
        for(int i=h[u];i;i=e[i].next)
        {
            int v=e[i].v,w=e[i].w;
            if(w==A)upd(v,dis[u][S]+w,S);
            else
            {
                if(getf(u)==getf(v))continue;
                if(~book[v]&&(S&(1<<book[v])))continue;
                upd(v,dis[u][S]+w,S|((~book[v])?1<<book[v]:0));
            }
        }
    }
    for(int i=1;i<=n;++i)
    {
        int ans=1<<30;
        for(int j=0;j<1<<tim;++j)ans=min(ans,dis[i][j]);
        printf("%d ",ans);
    }
    return 0;   
}

E. Election Promises

有一個\(DAG\),如今兩我的輪流操做。每次操做能夠隨意指定一個權值不爲\(0\)的點,而後把它的權值減少爲任意非負整數,同時能夠把其全部出邊的點權任意修改。不能操做者輸。
求先手是否必勝。generator

首先盲猜確定和\(sg\)函數那套理論相關,而後把每一個點的\(sg\)值給求出來。
題目的結論是:對於全部\(sg=i\)的點,咱們令\(s_k=\oplus_{sg[i]=k}h[i]\),若是存在一個\(s_k\neq 0\)的話那麼先手必勝,不然先手必敗。string

證實(僞證)
首先\(h\)全是\(0\)的時候必定是先手必敗。
對於一個\(sg=k\)的點\(u\),其兒子中一定包含了\([0,k-1]\)這些\(sg\)值。那麼咱們找到最大的\(sg\)值,其必定只能影響全部比他小的\(sg\)值的位置。
那麼咱們修改這個\(sg\)值中\(h\)最大的那個點,那麼一定可讓它的\(sg\)值減少,而後必定能夠把全部的\(s\)所有變成\(0\),因而咱們變成了一個必敗態。
然後手此時操做完以後又必定存在至少一個數是\(0\),又成了必勝態。

#include<iostream>
#include<cstdio>
#include<vector>
using namespace std;
#define MAX 200200
inline int read()
{
    int x=0;bool t=false;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}
vector<int> E[MAX];
int n,m,h[MAX],sg[MAX],sum[MAX];
int dg[MAX],Q[MAX],vis[MAX];
void Topsort()
{
    int h=1,t=0;
    for(int i=1;i<=n;++i)if(!dg[i])Q[++t]=i;
    while(h<=t){int u=Q[h++];for(int v:E[u])if(!--dg[v])Q[++t]=v;}
}
int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i)h[i]=read();
    for(int i=1;i<=m;++i)
    {
        int u=read(),v=read();
        E[u].push_back(v);
        ++dg[v];
    }
    Topsort();
    for(int i=n;i;--i)
    {
        int u=Q[i];
        for(int v:E[u])vis[sg[v]]=i;
        while(vis[sg[u]]==i)++sg[u];
        sum[sg[u]]^=h[u];
    }
    for(int i=n;~i;--i)
        if(sum[i])
        {
            int pos;
            for(int j=1;j<=n;++j)
                if(sg[j]==i&&h[j]>(sum[i]^h[j]))pos=j;
            h[pos]^=sum[i];
            for(int v:E[pos])h[v]^=sum[sg[v]],sum[sg[v]]=0;
            puts("WIN");
            for(int j=1;j<=n;++j)printf("%d ",h[j]);
            puts("");return 0;
        }
    puts("LOSE");
    return 0;
}
相關文章
相關標籤/搜索