其他分享
首页 > 其他分享> > 20220722

20220722

作者:互联网

染色

要求环上没有重复点,可以先求个点双。对每个点双分类讨论:

实现上注意有重边,应该令割点属于 dfs 树上最浅的点双,边属于 dfs 树上较深的点双

code
const int N = 5e5+5;
int n,m,mm=1,ind,dcc,head[N],nxt[N*4],to[N*4],dfn[N],low[N],in[N],siz1[N],siz2[N];
mint k,ans(1);

auto C=[](mint n,int m) {
	mint res(1);
	For(i,1,m) res *= (n-i+1) / i;
	return res;
};

void tj(int u) {
	static int stk[N],tp;
	dfn[u] = low[u] = ++ind, stk[++tp] = u;
	for(int i = head[u], v; v = to[i], i; i = nxt[i])
		if( dfn[v] ) ckmin(low[u],dfn[v]);
		else {
			tj(v), ckmin(low[u],low[v]);
			if( dfn[u] <= low[v] ) {
				++dcc, ++siz1[dcc];
				do in[stk[tp--]] = dcc, ++siz1[dcc]; while( stk[tp+1] != v );
			}
		}
}

signed main() { freopen("painting.in","r",stdin); freopen("painting.out","w",stdout);
	io>>n>>m>>k.x;
	For(i,1,m, x,y)
		io>>x>>y,
		nxt[++mm] = head[x], to[mm] = y, head[x] = mm,
		nxt[++mm] = head[y], to[mm] = x, head[y] = mm;
	For(i,1,n) if( !dfn[i] ) tj(i), in[i] = inf;
	for(int i = 2; i < mm; i += 2) ++siz2[min(in[to[i]],in[to[i^1]])];
	For(i,1,dcc)
		if( siz2[i] == 1 ) ans *= k;
		else if( siz1[i] == siz2[i] ) {
			mint res;
			Rep(j,0,siz2[i]) res += Pow(k,__gcd(j,siz2[i]));
			ans *= res / siz2[i];
		} else ans *= C(k+siz2[i]-1,siz2[i]);
	io<<ans.x;
	return 0;
}

序列计数问题

经典题,以前见过但没做。。。

先把 \(n,c\) 都 \(-1\)

考虑容斥,枚举超过限制的 \(x\),答案即为

\[\sum_{s\subseteq U}(-1)^{|s|}{n+m-c|s|-\sum_{i\in s}b^{i}\choose m} \]

枚举 \(|s|\),那么 \(a=n+m-c|s|\) 是定值。在 \(x=\sum_{i\in s}b^{i}\le a\) 的情况下 \({a-x\choose m}\) 是关于 \(x\) 的 \(m\) 次多项式,\(O(m^{2})\) 暴力计算出系数后代入 \(\sum x\) 即可求出 \(\sum{a-x\choose m}\)

剩下的问题是计算 \(x\) 的次幂。可以枚举 \(x\) 与 \(a\) 在 \(b\) 进制下 LCP 的下一位(注意到 \(x\) 在 \(b\) 进制下每位为 \(0/1\)),当前位 \(x<a\),那么低位可以随便填。预处理 \(g[i,j,k]\) 表示后 \(i\) 位,有 \(j\) 位为 \(1\),和的 \(k\) 次幂。使用二项式定理转移和合并高低位即可

实现上可以记录 \(c\) 的正负,并在高精减前判断大小,这样就不用写高精负数了。细节比较多

时间复杂度 \(O(m^{3}\log n)\)

code

计算机

sub 1

读题分,手玩即可

out
NOT 2 2
RSH 2 61
LSH 2 3
NOT 3 3
RSH 3 62
LSH 3 9
NOT 4 4
RSH 4 63
LSH 4 1
SET 1 2
OR 3 3 4
OR 1 1 3

sub 2

用 \(a_{i},b_{i}\) 表示两个数二进制第 \(i\) 位的值,\(c_i\) 表示第 \(i\) 位是否进位,那么 \(a+b=a\oplus b\oplus2c\)

注意到并行对分治结构是利好的,比如线段树建树复杂度变为 \(\log 64\)。如果能把 \(c\) 表示成具有结合律的信息,就可以类似 DDP 一样挂到线段树上。

考虑 \(c\) 的递推过程:

考虑对每个区间维护 \(t0[i],t1[i]\) 表示 \(c_{l-1}=0/1\) 时 \(c_{i}\) 的值,合并时使用 \(t0[mid],t1[mid]\) 更新 \([mid+1,r]\) 的值即可

code
int n=4,a[64],b[64],t0[64],t1[64];
uLL ans,x[401];

void SET(int i,int j) { x[i] = x[j]; printf("SET %d %d\n",i,j); }
void XOR(int i,int j,int k) { x[i] = x[j]^x[k]; printf("XOR %d %d %d\n",i,j,k); }
void AND(int i,int j,int k) { x[i] = x[j]&x[k]; printf("AND %d %d %d\n",i,j,k); }
void OR(int i,int j,int k) { x[i] = x[j]|x[k]; printf("OR %d %d %d\n",i,j,k); }
void LSH(int i,int j) { x[i] <<= j; printf("LSH %d %d\n",i,j); }
void RSH(int i,int j) { x[i] >>= j; printf("RSH %d %d\n",i,j); }
void NOT(int i,int j) { x[i] = ~x[j]; printf("NOT %d %d\n",i,j); }

#define mid (l+r>>1)
void bld(int l,int r) {
	if( l == r ) return;
	bld(l,mid), bld(mid+1,r);
	For(i,mid+1,r)
		AND(3,t1[mid],t1[i]), AND(4,t0[mid],t1[i]),
		OR(t1[i],t0[i],3), OR(t0[i],t0[i],4);
}
void qry(int l,int r) {
	if( l == r ) {
		if( l ) SET(b[l],t0[l-1]), LSH(b[l],l);
		else SET(b[l],2);
		return;
	}
	qry(l,mid), qry(mid+1,r), XOR(b[l],b[l],b[mid+1]);
}

signed main() { freopen("cal2.out","w",stdout);
	scanf("%llu %llu",&x[1],&x[2]), ans = x[1]+x[2];
	NOT(400,400), RSH(400,63);
	Rep(i,0,64)
		a[i] = ++n, SET(n,1), RSH(n,i), AND(n,n,400),
		b[i] = ++n, SET(n,2), RSH(n,i), AND(n,n,400);
	Rep(i,0,64) t0[i] = ++n, AND(t0[i],a[i],b[i]), t1[i] = a[i], OR(t1[i],a[i],b[i]);
	bld(0,63), qry(0,63);
	XOR(1,1,b[0]);
	assert(n<=400), assert(ans==x[1]);
	return 0;
}

sub 3

标签:int,mid,void,20220722,t0,t1,siz2
来源: https://www.cnblogs.com/401rk8/p/16536493.html