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
问有多少起始点,可以使小团子从起始点出发,存在一条路线,可以调好所有点的旋钮,
特别地,从起始点出发时,小团子并不会旋转起始点的旋钮
官方题解
先令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;
}