【NOI OL #1】序列

题目链接

首先我们发现,各个位置之间,$a$和$b$的原数值并不重要,所以我们可以令$b_i\leftarrow b_i-a_i$,接下来只使用这个差值。

然后我们发现,2操作是可以使两个位置之间的差值进行“流动”。

所以,如果只有2操作,把每个位置看成点,差值是点权,那么2操作就是随意转运权值的无向边,2操作构成的连通块的点权能够全部变为$0$等价于点权和为$0$。

然后加入1操作,发现它可以使相邻的两个点的点权和增减$2$的倍数。

然后我们只会做2操作。

这里是个我的思维瓶颈,考试的时候没突破,人裂开。

这时候就体现了手玩数据的好处了。我一直没这个习惯,导致有些挺显然的结论看不出来。

然后可以发现,1操作构成的路径(不一定是简单路径)上,距离为奇数的两个点可以同增同减,距离为偶数的两个点可以转运权值。

思考一下,如果只有1操作,那么这个路径的性质就迫使我们进行染色。染色……二分图来了。

如果某个连通块是个二分图,或者说,没有奇环,那么只要两个分图的权值和相同,就可以对消了。

那有奇环呢?发现每条路径只需要绕一下这个奇环,就可以改变自身的奇偶性,那就是都可以随意转运权值,要连通块权值和为$0$咯?也不全对,因为我们还可以利用奇数长度路径的性质,对整个连通块的点权和增减$2$的倍数。也就是说只要连通块点权和是偶数,就可以使每个点的点权变为$0$。

然后考虑怎么联合两种操作。

我们发现2操作连出来的连通块,内部的权值是可以随意转运的,那么对于一条1边(1操作连出来的边),固定了一个端点,另一个端点连在某个2边连通图里,不论连哪个点都是等价的。那么我们可以对2边连通块进行缩点,然后1边就只连2边连通块就行了。如果出现1边连了同一个2边连通块,直接当做奇环就可以了。

讲完了。嗯。记得开long long,然后常数写好一点。

我交过一份95分的代码,超时一个点。然后删了一句数组初始化为$0$的memset就卡过了。

 

代码(100分):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#define IL inline
#define RG register
#define _1 first
#define _2 second
using namespace std;
typedef long long LL;
typedef unsigned int UI;
const int N=1e5;

    int T,n,m;
    LL a[N+3];
    int k[3],u[3][N+3],v[3][N+3];
    vector<int>g[N+3];
    bool flag;
    int cnt,bel[N+3];
    LL sum[N+3];
    
void dfs1(int u){
    for(UI i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(bel[v]!=0)
            continue;
        
        bel[v]=bel[u];
        sum[bel[u]]+=a[v];
        dfs1(v);
        
    }
    
}
    
IL void sol1(){
    for(int i=1;i<=k[2];i++){
        g[u[2][i]].push_back(v[2][i]);
        g[v[2][i]].push_back(u[2][i]);
        
    }
    
    memset(bel,0,(n+3)*(sizeof(int)));
    cnt=0;
    for(int i=1;i<=n;i++)
    if(bel[i]==0){
        bel[i]=++cnt;    sum[cnt]=a[i];
        dfs1(i);
        
    }
    
    flag=true;
    for(int i=1;i<=cnt&&flag;i++)
    if(sum[i]!=0)
        flag=false;
    if(flag)
        printf("YES\n");
    
}

    bool tag[N+3],ex;
    int col[N+3];
    LL s[3];
    
void dfs2(int u){
    for(UI i=0;i<g[u].size();i++){
        int v=g[u][i];
        if(col[v]!=0){
            if(col[u]==col[v])
                ex=true;
            continue;
            
        }
        
        col[v]=3-col[u];
        if(tag[v])ex=true;
        s[col[v]]+=sum[v];
        dfs2(v);
        
    }
    
}

IL void sol2(){
    memset(tag,0,(cnt+3)*(sizeof(int)));
    for(int i=1;i<=cnt;i++)
        g[i].clear();
    
    for(int i=1;i<=k[1];i++)
    if(bel[u[1][i]]==bel[v[1][i]])
        tag[bel[u[1][i]]]=true;
    else {
        g[bel[u[1][i]]].push_back(bel[v[1][i]]);
        g[bel[v[1][i]]].push_back(bel[u[1][i]]);
    }
    
    memset(col,0,(cnt+3)*(sizeof(int)));
    flag=true;
    for(int i=1;i<=cnt;i++)
    if(col[i]==0){
        ex=tag[i];    s[2]=0;
        col[i]=1;    s[1]=sum[i];
        dfs2(i);
        if(ex)
            flag&=((s[1]+s[2])&1)==0;
        else 
            flag&=s[1]==s[2];
        
    }
    
    if(flag)
        printf("YES\n");
    else 
        printf("NO\n");
    
}
    
IL void sol(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    for(int i=1;i<=n;i++){
        LL x;    scanf("%lld",&x);
        a[i]=x-a[i];
        g[i].clear();
    }
    memset(g,0,sizeof g);
    k[1]=k[2]=0;
    for(int i=1;i<=m;i++){
        int t;
        scanf("%d",&t);
        k[t]++;
        scanf("%d%d",&u[t][k[t]],&v[t][k[t]]);
        
    }
    
    flag=false;
    sol1();
    if(flag)return ;
    sol2();
    
}

int main(){
    scanf("%d",&T);
    while(T--)
        sol();

    return 0;

}
View Code

 

posted @ 2020-05-29 10:04  汉谡  阅读(138)  评论(0编辑  收藏  举报