题解 连通性

传送门

考场上有刚过这题但没刚出来

猜测正解应该是DP了一个 \(f[i][j]\) 表示 \(n=i, m=j\) 时的答案
于是想这个东西怎么转移
首先可以对两类点黑白染色
如果令前面的点为白点,后面的点为黑点的话
发现最后一个黑色连通块一定是与一个白色连通块完全相连或不与白色连通块相连
特别注意一下一个白色连通块可能连通了几个黑色连通块
\(g[i]\) 为一个大小为 \(i\) 的连通块的方案数于是一个转移是

\[f[i][j]+=\sum\limits_{k=1}^{i-1} f[i-k][j-k]*g[k] \]

然后考虑另一种转移
枚举白色连通块和其连通的黑色连通块的大小
黑点限制其包含点 \(n\),白点不加限制
于是考虑每个黑点至少向一个白点连边,黑点间的边怎么连都行
白点必须形成恰好一个连通块

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline ll read() {
	ll ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
ll a[N], b[N];

void exgcd(ll a, ll b, ll& x, ll& y) {
	if (!b) {x=1; y=0; return ;}
	exgcd(b, a%b, y, x);
	y-=(a/b)*x;
}

ll crt() {
	ll M=1, ans=0;
	for (int i=1; i<=n; ++i) M*=b[i];
	for (int i=1; i<=n; ++i) {
		ll w=M/b[i], x, y;
		exgcd(w, b[i], x, y);
		ans=(ans+a[i]*w*x%M)%M;
	}
	return (ans%M+M)%M;
}

signed main()
{
	n=read();
	for (int i=1; i<=n; ++i) b[i]=read(), a[i]=read();
	printf("%lld\n", crt());
	
	return 0;
}
posted @ 2021-10-25 07:10  Administrator-09  阅读(1)  评论(0编辑  收藏  举报