当前位置: 代码迷 >> 综合 >> [Atcoder SoundHound Contest 2018]E.+ Graph
  详细解决方案

[Atcoder SoundHound Contest 2018]E.+ Graph

热度:120   发布时间:2023-10-22 22:21:52.0

题面

Time limit : 2sec / Memory limit : 1024MB
Score : 600 points

Problem Statement-题目描述

Kenkoooo found a simple connected graph. The vertices are numbered 111 through nnn. The iii-th edge connects Vertex uiu_iui? and viv_ivi?, and has a fixed integer sis_isi?.
Kenkoooo is trying to write a positive integer in each vertex so that the following condition is satisfied:

For every edge iii, the sum of the positive integers written in Vertex uiu_iui? and viv_ivi? is equal to sis_isi?.

Find the number of such ways to write positive integers in the vertices.

Constraints-数据范围

2≤n≤1052≤n≤10^52n105
1≤m≤1051≤m≤10^51m105
1≤ui&lt;vi≤n1≤u_i&lt;v_i≤n1ui?<vi?n
2≤si≤1092≤s_i≤10^92si?109
If i≠ji≠ji??=j, then ui≠uju_i≠u_jui???=uj? or vi≠vj.v_i≠v_j.vi???=vj?.
The graph is connected.
All values in input are integers.

题目大意

给你一个无向图G=(V,E)G=(V,E)G=(V,E),定义任意一条边e=(i,j)e=(i,j)e=(i,j) (i≠j,i∈V,j∈V)(i≠j,i∈V,j∈V)(i??=j,iV,jV)的权值为wi,jw_{i,j}wi,j?,任意一个点ci(c∈V)c_i ( c∈V )ci?(cV)的权值为wiw_iwi?,且**wiw_iwi?为正整数**,现在给出所有边eee和边的权值wi,jw_{i,j}wi,j?,求所有边与点都满足wi,j=wi+wjw_{i,j}=w_i+w_jwi,j?=wi?+wj?时,1号结点有多少种不同的取值。
2≤n≤1052≤n≤10^52n105
1≤m≤1051≤m≤10^51m105
1≤ui&lt;vi≤n1≤u_i&lt;v_i≤n1ui?<vi?n
2≤si≤1092≤s_i≤10^92si?109

题解

1.确定&方向

学过差分约束的同学可能会感觉这题与差分约束十分类似,是的。差分约束可以维护ai?aj≤Xa_i-a_j≤Xai??aj?X的情况,同时,ai?aj=Xa_i-a_j=Xai??aj?=X也可以转成ai?aj≤Xa_i-a_j≤Xai??aj?X进行差分约束。但题目的要求是ai+aj=Xa_i+a_j=Xai?+aj?=X至少在蒟蒻的知识范围内,是无法转化(或者很难转化)的。
这时候我们就又需要从暴力开始思考了。首先考虑枚举的变量应该是什么。
我们以样例2作为例子

4 3
1 2 6
2 3 7
3 4 5

[Atcoder SoundHound Contest 2018]E.+ Graph
显然我们可以将这些条件化成方程组
{a1+a2=6a2+a3=7a3+a4=5\begin{cases} a_1+a_2=6\\ a_2+a_3=7\\ a_3+a_4=5\\ \end{cases} ??????a1?+a2?=6a2?+a3?=7a3?+a4?=5?

我们很快可以发现只要我们知道其中的任意一个值,我们就可以求出所有的aaa
我们不妨直接枚举a1a_1a1?,判断它是否成立即可。
然而这样做一定是超时的,因此我们需要一个更好的方法来节省时间。
对方程比较敏感的同学很快会忍不住将1,21,21,2式相减得出a1?a3=?1a_1-a_3=-1a1??a3?=?1,再将其与333式相加得出a1+a4=4a_1+a_4=4a1?+a4?=4
这样一来,我们就求出a1a_1a1?与其他aaa的关系。
但是这有何用呢。
再看看题目,题目要求ai&gt;0a_i&gt;0ai?>0。显然我们需要往求范围的方向想问题。
因为a2=6?a1&gt;0a2=6-a_1&gt;0a2=6?a1?>0,a3=a1+1&gt;0a_3=a_1+1&gt;0a3?=a1?+1>0,a4=4?a1&gt;0a_4=4-a_1&gt;0a4?=4?a1?>0
我们就可以得到a1&lt;6a_1&lt;6a1?<6,a1&gt;?1a_1&gt;-1a1?>?1,a1&lt;4a_1&lt;4a1?<4,和a1&gt;0a_1&gt;0a1?>0结合得0&lt;a1&lt;40&lt;a_1&lt;40<a1?<4a1a_1a1?333个取值。没错,这就是正确答案了。
根据上面的操作我们可以发现几个有用的规律

1.当n≡0(&VeryThinSpace;mod&VeryThinSpace;2)n≡0(\bmod 2)n0(mod2)(即n%2=0)时,总有a1+an=Aa_1+a_n=Aa1?+an?=A,换成不等式即a1&lt;Aa_1&lt;Aa1?<A
2.当n≡1(&VeryThinSpace;mod&VeryThinSpace;2)n≡1(\bmod 2)n1(mod2)(即n%2=1)时,总有a1?an=Ba_1-a_n=Ba1??an?=B,换成不等式即a1&gt;Ba_1&gt;Ba1?>B

显然,答案a1a_1a1?的范围即为max?(B1,B2,...,Bk1,0)&lt;a1&lt;min?(A1,A2,...,Ak2)\max(B_1,B_2,...,B_{k_1},0)&lt;a_1&lt;\min(A_1,A_2,...,A_{k_2})max(B1?,B2?,...,Bk1??,0)<a1?<min(A1?,A2?,...,Ak2??)
直接用DFS/BFSDFS/BFSDFS/BFS遍历整张图很容易求出范围与a1a_1a1?的合法个数。
难道这就是这道题的正解?

2.尝试&完善

作为这场比赛的最后一题,绝对不可能这么简单。
上面的样例显然遗漏了我们的老朋友(233)——环。
这个关系在环上也成立吗?
让我们来看看第一组样例吧。

3 3
1 2 3
2 3 5
1 3 4

[Atcoder SoundHound Contest 2018]E.+ Graph
(莫名画成直角三角形…)
我们再写出它的对应方程组:
{a1+a2=3a2+a3=5a1+a3=4\begin{cases} a_1+a_2=3\\ a_2+a_3=5\\ a_1+a_3=4\\ \end{cases} ??????a1?+a2?=3a2?+a3?=5a1?+a3?=4?

求出它的对应式子
a1+a2=3a_1+a_2=3a1?+a2?=3
a1?a2=?1a_1-a_2=-1a1??a2?=?1
a1?a3=?2a_1-a_3=-2a1??a3?=?2
a1+a3=4a_1+a_3=4a1?+a3?=4

咦?..怎么每个aaa都有两个关系式?
实质上,是因为每个点都可以通过节点111,通过两条不同的路径到达节点nnn所造成的。
可以发现,每个aaa所拥有的两个关系式都可以相加直接求出
唯一的
a1a_1a1?
即设a1+an=Aa_1+a_n=Aa1?+an?=A,a1?an=Ba_1-a_n=Ba1??an?=B,那么a1=A+B2a_1=\frac{A+B}{2}a1?=2A+B?

那么环都能直接能确定a1a_1a1?吗?
实际上,是不行的。
[Atcoder SoundHound Contest 2018]E.+ Graph
例如上图中的例子,a1a_1a1?就有三个合法解1,2,31,2,31,2,3(具体求法可以参考第111部分的方程组)。
实质上是因为从节点111节点nnn的两条路径都是奇数,以至于两者的关系式不仅符号相同,连数值也相同。(使用111部分中的规律)
也就相当于一个式子了。

经过一系列的归纳与整理,我们发现:只要是奇环(即环的节点数有奇数个),就能求出唯一的a1a_1a1?,反之则不行(当然,在本题中,这并不是一个重要的结论)

这就是这道题的正解?
好像还漏了些什么…

3.排查&漏洞

没错…还有无解的情况
让我们返回样例111看看。
我们说过对于每一个aaa,都能确定唯一一个a1a_1a1?
但在无解的情况下它仍然成立吗?
肯定不一定。
因此,要判断无解,首先要确定通过其他aaa求出的每个a1a_1a1?都相同。这是第一。

还有,前文说道环上的每个结点对于a1a_1a1?都有两条路径。但如果有多个环,那就有多个关系式,那就有许多关系式相同的情况。如果当他们的关系式符号相同时(即an+a1=Aa_n+a_1=Aan?+a1?=A,an+a1=Ba_n+a_1=Ban?+a1?=B或者an?a1=Aa_n-a_1=Aan??a1?=A,an?a1=Ba_n-a_1=Ban??a1?=B),我们一定能得出A=BA=BA=B的结论,但如果A≠BA≠BA??=B,显然无解。这是第二。

第三是最简单的,即111部分中说提到的范围max(B1,B2,...,Bk1,0)&lt;a1&lt;min(A1,A2,...,Ak2)max(B_1,B_2,...,B_{k_1},0)&lt;a_1&lt;min(A_1,A_2,...,A_{k_2})max(B1?,B2?,...,Bk1??,0)<a1?<min(A1?,A2?,...,Ak2??)无解。
A=min(A1,A2,...,Ak2)A=min(A_1,A_2,...,A_{k_2})A=min(A1?,A2?,...,Ak2??),B=max(B1,B2,...,Bk1,0)B=max(B_1,B_2,...,B_{k_1},0)B=max(B1?,B2?,...,Bk1??,0),那么当A?B?1≤0A-B-1≤0A?B?10时,a1a_1a1?无解。
这就是这道题的正解?

4.汇集&实现

没错,这就是这题的正解(QWQQWQQWQ)
至于如何BFS/DFSBFS/DFSBFS/DFS,一般只有遍历顺序的问题,这里直接将记录关系式(不等式)的问题与遍历问题结合,即vis[N][2]vis[N][2]vis[N][2]
,这里vis[N][0]vis[N][0]vis[N][0]中存的是a1&gt;Ba_1&gt;Ba1?>B中的?B-B?B(看后面代码的时候一定要注意这一点),目的是更简单直观vis[N][1]vis[N][1]vis[N][1]中存的是a1&lt;Aa_1&lt;Aa1?<A中的AAA;
然而还有一些细节需要提一下

1.图是双向的(蒟蒻没看清楚以至于调了2天的代码…)
2.222部分中,用奇环求a1a_1a1?时最好放在DFS/BFSDFS/BFSDFS/BFS后单独求解
3.222部分中,用奇环求出的a1a_1a1?一定要满足111部分中说提到的范围max(B1,B2,...,Bk1,0)&lt;a1&lt;min(A1,A2,...,Ak2)max(B_1,B_2,...,B_{k_1},0)&lt;a_1&lt;min(A_1,A_2,...,A_{k_2})max(B1?,B2?,...,Bk1??,0)<a1?<min(A1?,A2?,...,Ak2??),否则无解
4.111部分中,一定要记得a1&lt;Aa_1&lt;Aa1?<A,a1&gt;Ba_1&gt;Ba1?>B,不是a1≤Aa_1≤Aa1?A,a1≥Ba_1≥Ba1?B

对了…BFS/DFSBFS/DFSBFS/DFS的时间复杂度是接近O(n)O(n)O(n)的。可以非常轻松地通过本题。
现在就是你们最喜欢的代码了233…
有点丑…

DFS版

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define MAXN 100000
#define MAXM 100000
#define INF 1000000000000000
struct node
{
    int next,to;long long w;
}e[MAXM*2+5];
struct Qr
{
    int xr,dec;long long lim;
};
long long T;
int cnt,m,n;
int x,y,z,flag;
long long ri=INF,le=-INF;
long long vis[2][MAXN+5];
int b[MAXN+5];
void fpush(int u,int v,long long w)
{
    e[++cnt].next=b[u];e[cnt].w=w;e[cnt].to=v;b[u]=cnt;
}
int Ch(int x){
    if(x==-1)x=0;return x;}
void dfs(int xr,int dec,long long lim)
{
    if(flag)return ;for(int i=b[xr];i;i=e[i].next){
    int xnext=e[i].to;long long nlim=e[i].w-lim;int FD=Ch(dec);if(vis[FD][xnext]!=INF){
    if(vis[FD][xnext]!=nlim){
    flag=1;return ;}else continue;}vis[FD][xnext]=nlim;if(!FD)le=max(le,-vis[FD][xnext]);else ri=min(ri,vis[FD][xnext]);dfs(xnext,-dec,nlim);}
}
int main()
{
    fill(vis[0],vis[0]+MAXN+1,INF);fill(vis[1],vis[1]+MAXN+1,INF);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&z);fpush(x,y,z);fpush(y,x,z);}dfs(1,1,0);if(flag){
    printf("0\n");return 0;}long long T=-1;for(int i=1;i<=n;i++)if(vis[0][i]!=INF&&vis[1][i]!=INF){
    long long NT=(vis[1][i]-vis[0][i])/2;if(NT<0){
    flag=1;break;}if(T==-1)T=NT;else if(T!=NT){
    flag=1;break;}}if(flag){
    printf("0\n");return 0;}if(T!=-1){
    if(T>le&&T<ri)printf("1\n");else printf("0\n");}else printf("%lld",max((long long)0,ri-le-1));
}

BFS版

#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define MAXN 100000
#define MAXM 100000
#define INF 1000000000000000
struct node
{
    int next,to;long long w;
}e[MAXM*2+5];
struct Qr
{
    int xr,dec;long long lim;
};
long long T;
int cnt,m,n;
int x,y,z,flag;
long long ri=INF,le=-INF;
long long vis[2][MAXN+5];
int b[MAXN+5];
void fpush(int u,int v,long long w)
{
    e[++cnt].next=b[u];e[cnt].w=w;e[cnt].to=v;b[u]=cnt;
}
int Ch(int x){
    if(x==-1)x=0;return x;}
int main()
{
    fill(vis[0],vis[0]+MAXN+1,INF);fill(vis[1],vis[1]+MAXN+1,INF);scanf("%d%d",&n,&m);for(int i=1;i<=m;i++){
    scanf("%d%d%d",&x,&y,&z);fpush(x,y,z);fpush(y,x,z);}queue<Qr> Q;Q.push((Qr){
    1,1,0});while(!Q.empty()){
    Qr A=Q.front();Q.pop();int xr=A.xr,dec=A.dec;long long lim=A.lim;for(int i=b[xr];i;i=e[i].next){
    int xnext=e[i].to;long long nlim=e[i].w-lim;int FD=Ch(dec);if(vis[FD][xnext]!=INF){
    if(vis[FD][xnext]!=nlim){
    printf("0\n");return 0;}else continue;}vis[FD][xnext]=nlim;if(!FD)le=max(le,-vis[FD][xnext]);else ri=min(ri,vis[FD][xnext]);Q.push((Qr){
    xnext,-dec,nlim});}}long long T=-1;for(int i=1;i<=n;i++)if(vis[0][i]!=INF&&vis[1][i]!=INF){
    long long NT=(vis[1][i]-vis[0][i])/2;if(NT<0){
    flag=1;break;}if(T==-1)T=NT;else if(T!=NT){
    flag=1;break;}}if(flag){
    printf("0\n");}if(T!=-1){
    if(T>le&&T<ri)printf("1\n");else printf("0\n");}else printf("%lld",max((long long)0,ri-le-1));
}

END

  相关解决方案