zoukankan      html  css  js  c++  java
  • AGC031F Walk on Graph

    AGC031F

    给你一个有(n)个点(m)条边的无向图。

    (Q)次询问,每次询问(S)(T),是否有路径的权值恰好为(R)

    路径权值的定义:(sum_{i=1}^k2^{i-1}c_i mod MOD)(c_i)表示经过的第(i)条边的边权。

    (MOD)给定。(MODle 10^6)

    (n,m,Qle 5*10^4)


    神仙题。

    首先感觉正着做要记第几个,不太优美,考虑反着做。记状态为((a,x))表示到达了点(a),权值为(x)。一次经过边((a,b,c))的转移可以到达((b,2x+c))

    接下来分析一波性质:

    性质一:

    假如在边((a,b,c))来回走动:((a,x) o (b,2x+c) o (a,4x+3c) o dots)。走(i)次权值为(2^ix+(2^i-1)c)。若干次(具体来说是(2)在模(MOD)意义下的秩次)后,权值就会回到(x)。这时候点可能在(a)(b),如果这个时候位置在(b),再走这么多次位置就会回到(a)

    于是可以发现((b,2x+c))是可以到达((a,x))的。那么((a,x))((b,2x+c))之间连的是双向边

    如果我们将所有状态建出一个图,那么问题就是判断((T,0))((S,R))是否在同一个连通块。

    性质二:

    假如现在的权值是(x),和当前所在的点(记为(u))相连的边有两条长度为(a)(b)的边。

    分别来回走一趟,分别可以得到(4x+3a)(4x+3b)

    换元,将(4x+3a)变成(x)那么(x)(x+3(b-a))是联通的

    将这个性质扩展一下:一个在远处的点(v),和它相连的有两条长度为(a)(b)的边。现在在点(u),权值(x)(x+3(b-a))是联通的。 考虑如此构造:任选一条从(u)(v)路径走过去,权值形如(2^kx+sum_{i=1}^k2^{i-1}c_i),然后根据前面的结论变成(2^kx+sum_{i=1}^k2^{i-1}c_i+2^k*3(b-a))。按照先前的路径回退回去(如性质一,即((b,2x+c))((a,x)))。这样最终到达状态((u,x+3(b-a)))

    再扩展:选取的这两条边可以是任意的两条边。 假如(u)(v)之间有一条权值为(b)的边,考虑选取和(u)相连的两条边(a,b)与和(v)相连的两条边(b,c),那么可以从(x)得到(x+3(b-a)+3(c-b)=x+3(c-a))。如果不直接相连,可以类似地扩展。

    性质三:

    (g=gcd_{a,bin E}(a-b))。其中(a-b)是模(MOD)意义下的。

    可以证明(g|MOD):由于(g|((a-b)mod MOD),g|((b-a) mod MOD)),所以(g|MOD)

    那么把(gcd(3g,MOD))作为新的模数,是可以替代原问题的。因为由数论知识得每个状态的权值可以任意加减(3g)

    显然(gcd(3g,MOD)=g或3g)

    接下来是做法:

    显然所有边的边权模(g)的值是相同的,记为(z)

    将所有边权减(z),状态中的权值加(z)

    可以发现这样转换之后问题是不变的:((a,x) o (a,x+z)' o (b,2(x+z)+c-z)'=(b,2x+c+z)' o (b,2x+c))

    (后面不把(')写出来了,这里只是为了方便区分)

    考虑从((u,x))出发,到达的状态一定可以表示成这样的形式:((v,2^px+qg))

    显然(qin {0,1,2})

    由于有((a,x) o (b,2x+c) o (a,4x+3c))(c)(g)的倍数,在对(gcd(3g,MOD))取模之后,就变成了((a,4x))

    因此(p)只需要记录它的奇偶性。因此取值范围为({0,1})

    总共有(6n)种状态,连边,并查集维护。

    在查询的时候,相当于从((T,z))出发到((S,R+z))

    询问是否存在(p,q)使得(R+zequiv 2^{2k+p}z+qg pmod {gcd(3g,MOD)},kin Z)

    预处理能表示为(2^{2k+p}z)的点有哪些,然后就可以快速判断了。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 50010
    int gcd(int a,int b){
    	while (b){
    		int k=a%b;
    		a=b,b=k;
    	}
    	return a;
    }
    int n,m,p;
    struct EDGE{
    	int to,c;
    	EDGE *las;
    } e[N*2];
    int ne;
    EDGE *last[N];
    void link(int u,int v,int c){
    	e[ne]=(EDGE){v,c,last[u]};
    	last[u]=e+ne++;
    }
    int g,d,z;
    int pz[2][1000010];
    struct Dsu{
    	Dsu *p;
    	Dsu *getfa(){return p==this?p:p=p->getfa();}
    } *f[N][2][3];
    bool check(int S,int T,int R){
    	for (int j=0;j<2;++j)
    		for (int k=0;k<3;++k){
    			int t=((z+R-k*g)%d+d)%d;
    			if (pz[j][t] && f[T][0][0]->getfa()==f[S][j][k]->getfa())
    				return 1;			
    		}
    	return 0;
    }
    int main(){
    	int Q;
    	scanf("%d%d%d%d",&n,&m,&Q,&p);
    	for (int i=1;i<=m;++i){
    		int u,v,c;
    		scanf("%d%d%d",&u,&v,&c);
    		link(u,v,c),link(v,u,c);
    	}
    	g=p;
    	for (int i=1;i<=n;++i){
    		int c1=last[i]->c;
    		for (EDGE *ei=last[i]->las;ei;ei=ei->las)
    			g=gcd(g,gcd((c1-ei->c+p)%p,(ei->c-c1+p)%p));
    	}
    	d=gcd(3*g,p);
    	z=last[1]->c%g;
    	for (int i=1;i<=n;++i)
    		for (EDGE *ei=last[i]->las;ei;ei=ei->las)
    			ei->c-=z;
    	for (int i=1;i<=n;++i)
    		for (int j=0;j<2;++j)
    			for (int k=0;k<3;++k){
    				f[i][j][k]=new Dsu;
    				f[i][j][k]->p=f[i][j][k];
    			}
    	pz[0][z]=1;
    	pz[1][z*2%d]=1;
    	if (z){
    		for (int x=z*4%d;x!=z;x=x*4%d)
    			pz[0][x]=1;
    		for (int x=z*8%d;x!=z*2%d;x=x*4%d)
    			pz[1][x]=1;
    	}
    	for (int i=1;i<=n;++i)
    		for (int j=0;j<2;++j)
    			for (int k=0;k<3;++k)
    				for (EDGE *ei=last[i];ei;ei=ei->las){
    					int j_=(j+1)%2,k_=(k*2+ei->c/g)%(d/g);
    					f[ei->to][j_][k_]->getfa()->p=f[i][j][k]->getfa();
    				}
    	while (Q--){
    		int S,T,R;
    		scanf("%d%d%d",&S,&T,&R);
    		if (check(S,T,R))
    			printf("YES
    ");
    		else
    			printf("NO
    ");
    	}
    	return 0;
    }
    
  • 相关阅读:
    thinkphp5 tp5 命名空间 报错 Namespace declaration statement has to be the very first statement in the script
    开启 php 错误 提示 php-fpm 重启 nginx 500错误 解决办法 wdlinux lnmp 一键包 php脚本无法解析执行
    js 设置 cookie 定时 弹出层 提示层 下次访问 不再显示 弹窗 getCookie setCookie setTimeout
    php 二维数组 转字符串 implode 方便 mysql in 查询
    nginx 重启 ps -ef|grep nginx kill -HUP 主进程号
    jquery bootstrap help-block input 表单 提示 帮助 信息
    jquery 倒计时 60秒 短信 验证码 js ajax 获取
    jQuery如何获取同一个类标签的所有的值 遍历
    linux下C语言文件操作相关函数
    gcc,gdb用法
  • 原文地址:https://www.cnblogs.com/jz-597/p/13653437.html
Copyright © 2011-2022 走看看