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;
}
原文地址:https://www.cnblogs.com/jz-597/p/14432560.html