The 17-th Beihang University Collegiate Programming Contest (BCPC 2022) - Preliminary L.逃跑路线(思维/二分图)

题目

n(2<=n<=1e6)个点m(n-1<=m<=1e6)条边的无向连通图,无自环,无重边,

每个点上有一个旋钮,刻度为1到k,

点i上旋钮的初始刻度为ai,现在为了调好,需要旋到bi(1<=ai,bi<=k)

小团子从点u走向点v的时候,会将点v的旋钮向后旋转一格,

即若当前刻度x小于k,则变为x+1;若当前刻度x等于k,则变为1

问有多少起始点,可以使小团子从起始点出发,存在一条路线,可以调好所有点的旋钮,

特别地,从起始点出发时,小团子并不会旋转起始点的旋钮

思路来源

官方题解

The 17-th Beihang University Collegiate Programming Contest (BCPC 2022) - Preliminary L.逃跑路线(思维/二分图)_第1张图片

题解

先令val[i]=b[i]-a[i],则令旋钮i往后旋转,等同于val[i]=(val[i]-1+k)%k

如果只有3个点的一条链u-v-w,

则可以直接一步走到v上,令v减1,然后走到w上,令w减1,

继续走到v上,再走到w上,重复这个过程,

直到v被减了k,此时再走到w上,w也被减了k,

从模意义上相当于没减,从u跳到了w

类似地,如果u需要减val[u],则可以在u和v之间转圈,

最终,u减到了0,v减掉了(val[u]+1)%k,此时站在v上,

类似地,v减掉k-(val[u]+1)%k,则可以在v和w之间转圈,

最终v减到了0,w减掉了(k-(val[u]+1)%k+1)%k,相当于加上了val[u]

即u的权值转移到了w,这表明二分图染色后,相同颜色的权值可以互相转

最后抽象成两个点的情形,就对应了题解的四种情况

记每个点的权重sum为同颜色点val之和,tot为同颜色点个数,有:

1.sum[黑]=sum[白],答案为n

2.sum[黑]=(sum[白]+1)%k,答案为tot[白]

3.sum[白]=(sum[黑]+1)%k,答案为tot[黑]

4.k=2,显然2和3同是满足,答案为n

还有第五种情况,无法二分图染色,即有奇环的情形

虽然黑白之间的挪动会差1,但最后权值一定能归到某个点上

让所有权值都归于奇环上的一个点,

不妨考虑最简单的三元环u-v-w,更多元奇环类似

假设此时权值全在u上,且此时站在u上

如果u需要减1,只需走到v上,令v减1,再走回u,令u减1,再走到w上,令w减1,

然后在v和w之间反复行走,直到均走够k的倍数即可,此时位于v和w的某一个点上,

如果u还需要减,则可以再走回u,相当于一次循环使u减2,一番操作之后sum[u]%2,

若sum[u]为奇数,在最后一轮时,在v和w的某一个点上停留即可

代码

#include
#include
#include
using namespace std;
const int N=1e6+10,mod=998244353,owt=(mod+1)/2;
int n,m,k,u,v,a,b[N],sum[3],tot[3];
int col[N];
vectore[N];
//1 2 二分图染色
bool dfs(int u,int fa,int c){
    col[u]=c;
    sum[c]=(sum[c]+b[u])%k;
    tot[c]++;
    for(auto &v:e[u]){
        if(v==fa)continue;
        if(col[v]==c)return 0;
        if(!col[v]){
            if(!dfs(v,u,c^3))return 0;
        }
    }
    return 1;
}
int solve(){
    if(k==2 || !dfs(1,0,1)){
        return n;
    }
    int u=1,v=2;
    if(sum[u]==sum[v]){
        return n;
    }
    if(sum[u]==(sum[v]+1)%k){
        return tot[v];
    }
    swap(u,v);
    if(sum[u]==(sum[v]+1)%k){
        return tot[v];
    }
    return 0;
}
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i){
        scanf("%d%d",&a,&b[i]);
        b[i]=(b[i]-a+k)%k;
    }
    for(int i=1;i<=m;++i){
        scanf("%d%d",&u,&v);
        e[u].push_back(v);
        e[v].push_back(u);
    }
    printf("%d\n",solve());
    return 0;
}

你可能感兴趣的:(思维题,#,图论基础,图论,二分图染色,思维题)