zoukankan      html  css  js  c++  java
  • CF1266H. Red-Blue Graph

    有个图,(1)(n-1)的点每个点有两条出边,分别为红色和蓝色。一开始人在点(1)且所有点的激活边为蓝色。(n)没有出边。

    每个时间点,当前点的激活边切换且人立即沿着切换后的激活边走下去。如果到达点(n)则停止。

    给出图和每个点的红蓝出边,接下来有若干个询问,询问某个状态((v,S)表示当前点为(v)(v<n)),激活边的状态为(S))是否可达。

    (nle 58,Qle 5000)

    保证从任意点出发存在路径到达点(n)。(此时不考虑上面走的规则,即走边的时候没有限制)


    最后才发现原来题目开(nle 58)似乎是因为(57*2^{57})long long范围内。

    结论:状态不会成环。

    假设成环,即从某个状态出发一直走最终回到了这个状态,设在这个过程中经过的点集为(S)。显然任意边((u,v)in E,uin S)都被走过一次,并且由(S)的定义得(vin S)。因此(S)中的点没有连向(S)外的点的边,成为一个极大的强联通分量,此时任意(S)内的点都不能经过有向边到达(n)

    考虑求出每条边被经过的次数。设(x_i)表示(R_i)的经过次数,(x_i-z_i)表示(B_i)的经过次数,以及(w_i)表达(i)的不经过(R_i,B_i)的额外流出(即如果(i)为当前点(w_i+1),如果(i)起点(w_i-1)),然后根据(F_{入}=F_{出})列出(n-1)个等式。(z_i)(w_i)由每个询问内容决定,所以把它们当做常数带入。于是就可以解出(x_i)的关于(z,w)的表达式。

    由于题目保证了状态不会成环,所以解出的(x_i)一定是唯一解的。

    合法的判定:

    1. (x_i)是非负整数。
    2. 对于所有至少经过一次的点,都可以只通过当前状态下的激活边到达当前点。必要性显然,充分性大概可以归纳。

    时间(O(n^3+qn^2))

    注意实现的时候如果用long double会爆精度。我的做法是搞两个(9*10^{18})级别的质数来算,如果答案是非负整数则两次结果相等,如果是负数或者是分数结果几乎不一定相等。


    using namespace std;
    #include <bits/stdc++.h>
    #define N 205
    #define ll long long
    #define ull unsigned long long
    #define db long double
    #define mo1 9000000000000000041
    #define mo2 9000000000000000053
    //ll mul(ll x,ll y,ll mo){
    //	return (__int128)x*y%mo;
    //}
    ll mul(ll x,ll y,ll mo){
    //	x%=mo,y%=mo;
    	ll t=x*y-(ll)floor((long double)x*y/mo)*mo;
    	if (t<0) t+=mo;
    	if (t>=mo) t-=mo;
    	return t;
    }
    //ull mul(ull x,ull y,ull mo){
    //	ull s=0;
    //	for (;y;y>>=1,x=(x<<1)%mo)
    //		if (y&1)
    //			s=(s+x)%mo;
    //	return s;	
    //}
    ll qpow(ll x,ll y,ll mo){
    	ll r=1;
    	for (;y;y>>=1,x=mul(x,x,mo))
    		if (y&1)
    			r=mul(r,x,mo);
    	return r;
    }
    int n;
    int b[N],r[N];
    ll o[N][N];
    struct Num{
    	ull p,q;
    	bool _0(){return p==0 && q==0;}
    	Num inv(){return {qpow(p,mo1-2,mo1),qpow(q,mo2-2,mo2)};}
    };
    Num operator+(Num x,Num y){return {(x.p+y.p)%mo1,(x.q+y.q)%mo2};}
    Num operator-(Num x,Num y){return {(x.p-y.p+mo1)%mo1,(x.q-y.q+mo2)%mo2};}
    Num operator*(Num x,Num y){return {mul(x.p,y.p,mo1),mul(x.q,y.q,mo2)};}
    Num operator*(ll x,Num y){return {mul((x+mo1)%mo1,y.p,mo1),mul((x+mo2)%mo2,y.q,mo2)};}
    Num v[N][N];
    char str[N];
    void calc(){
    	for (int i=1;i<=n;++i){
    		Num inv=v[i][i].inv();
    		for (int k=i;k<=n*3;++k)
    			v[i][k]=v[i][k]*inv;
    		for (int j=i+1;j<=n;++j)
    			if (!v[j][i]._0()){
    				Num tmp=v[j][i];
    				for (int k=i;k<=n*3;++k)
    					v[j][k]=v[j][k]-v[i][k]*tmp;
    			}
    	}
    	for (int i=n;i>=1;--i)
    		for (int j=1;j<i;++j)
    			if (!v[j][i]._0()){
    				for (int k=n+1;k<=n*3;++k)
    					v[j][k]=v[j][k]-v[j][i]*v[i][k];
    				v[j][i]={0,0};
    			}
    }
    int z[N],w[N];
    ll x[N];
    ll doit(int c){
    	for (int i=1;i<=n;++i)
    		if (r[i]==n+1 && str[i]=='R')
    			return -1;
    	ll s=0;
    	for (int i=1;i<=n;++i){
    		Num t={0,0};
    		for (int j=1;j<=n;++j)
    			t=t-(z[j]*v[i][n+j]+w[j]*v[i][n+n+j]);
    		if (t.p!=t.q)
    			return -1;
    		x[i]=t.p;
    		if (x[i]-z[i]<0)
    			return -1;
    		s+=2*x[i]-z[i];
    	}
    	static int vis[N],BZ;
    	for (int i=1;i<=n;++i)
    		if (x[i]>0){
    			++BZ;
    			for (int y=i;vis[y]!=BZ;y=(str[y]=='R'?r[y]:b[y]))
    				vis[y]=BZ;
    			if (vis[c]!=BZ)
    				return -1;
    		}
    	return s;
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d",&n);
    	n--;
    	w[1]--;
    	for (int i=1;i<=n;++i){
    		o[i][i]-=2;
    		o[i][n+i]++;
    		o[i][n+n+i]--;
    	}
    	for (int i=1;i<=n;++i){
    		scanf("%d%d",&b[i],&r[i]);
    		if (b[i]<=n) o[b[i]][i]++,o[b[i]][n+i]--;
    		if (r[i]<=n) o[r[i]][i]++;
    	}
    	for (int i=1;i<=n;++i)
    		for (int j=1;j<=n*3;++j)
    			v[i][j]={(o[i][j]+mo1)%mo1,(o[i][j]+mo2)%mo2};
    	calc();
    	int Q;
    	scanf("%d",&Q);
    	while (Q--){
    		int c;
    		scanf("%d%s",&c,str+1);
    		assert(c<=n);
    		w[c]++;
    		for (int i=1;i<=n;++i)
    			z[i]=(str[i]=='R'?1:0);
    		ll ans=doit(c);
    		printf("%lld
    ",ans);
    		w[c]--;
    	}
    	return 0;
    }
    
  • 相关阅读:
    Doubles
    The 3n + 1 problem
    Counterfeit Dollar
    Maya Calendar
    08_python的列表、元祖、字符串、字典及公共方法
    07_python的函数
    06_python的while语句
    05_python的if语句
    04_python基本的输入输出函数
    03_python的数据类型和变量的定义及使用
  • 原文地址:https://www.cnblogs.com/jz-597/p/14432560.html
Copyright © 2011-2022 走看看