BZOJ 1547: 周末晚会

首先根据旋转同构,可以用burnside
\(dp(d)\) 为长度为 \(d\) 的环,旋转不同构的方案
那么答案为 \(\dfrac{\sum \limits_{i=0}^{n-1} dp(\gcd(n, i))}{n}=\sum \limits_{d | n} \dfrac{dp(d)\varphi(\frac{n}{d})}{n}\)
现在要求 \(dp(d)\)
\(d \leq k \wedge k < n\) 时,只要不全部都是女生即可,方案数为 \(2^d - 1\)
\(d \leq k \wedge n \leq k\) 时,怎么放都不可能超过k,方案数为 \(2^d\)
否则就 dp 求解
\(f_{i,j}\) 表示长度为 \(i\) 的区间,末尾有 \(j\) 个女生的方案
\(g_{i,j}\) 表示长度为 \(i\) 的区间,首位必须是男生,末尾有 \(j\) 个女生的方案
转移很简单
要得到 \(dp(i)\),就用 \(\sum \limits_{j=0}^{\min(i, k)}f_{i, j}\) 减去不合法的方案
不合法的方案只可能首尾多个女生相接超过 \(k\) 的情况
首尾最多各能放 \(k\) 个女生,则放的个数 \(k+1 \to 2k\)
可以推得不合法的方案为 \(\sum \limits_{j=k+1}^{\min(2k, i-1)} g_{i-j, 0} \times (2k - j + 1)\)
\(O(n^2)\) 预处理出 \(dp(i)\) 的值

#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define pii pair<int, int>
#define pli pair<ll, int>
#define lp p << 1
#define rp p << 1 | 1
#define mid ((l + r) / 2)
#define lowbit(i) ((i) & (-i))
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define rep(i,a,b) for(int i=(a);i<(b);i++)
#define per(i,a,b) for(int i=((b)-1);i>=(a);i--)
#define Edg int ccnt=1,head[N],to[E],ne[E];void addd(int u,int v){to[++ccnt]=v;ne[ccnt]=head[u];head[u]=ccnt;}void add(int u,int v){addd(u,v);addd(v,u);}
#define Edgc int ccnt=1,head[N],to[E],ne[E],c[E];void addd(int u,int v,int w){to[++ccnt]=v;ne[ccnt]=head[u];c[ccnt]=w;head[u]=ccnt;}void add(int u,int v,int w){addd(u,v,w);addd(v,u,w);}
#define es(u,i,v) for(int i=head[u],v=to[i];i;i=ne[i],v=to[i])
const int MOD = 100000007;
void M(int &x) {if (x >= MOD)x -= MOD; if (x < 0)x += MOD;}
int qp(int a, int b = MOD - 2) {int ans = 1; for (; b; a = 1LL * a * a % MOD, b >>= 1)if (b & 1)ans = 1LL * ans * a % MOD; return ans % MOD;}
template<class T>T gcd(T a, T b) { while (b) { a %= b; std::swap(a, b); } return a; }
template<class T>bool chkmin(T &a, T b) { return a > b ? a = b, 1 : 0; }
template<class T>bool chkmax(T &a, T b) { return a < b ? a = b, 1 : 0; }
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline char getc() {
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline int _() {
	int x = 0, f = 1; char ch = getc();
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getc(); }
	while (ch >= '0' && ch <= '9') { x = x * 10ll + ch - 48; ch = getc(); }
	return x * f;
}

const int N = 2222;
int f[N][N], g[N][N], dp[N];
int prime[N], prin, phi[N], n, k;

void init() {
	static bool vis[N];
	phi[1] = 1;
	rep (i, 2, N) {
		if (!vis[i]) prime[++prin] = i, phi[i] = i - 1;
		rep (j, 1, prin + 1) {
			if (i * prime[j] >= N) break;
			vis[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
				phi[i * prime[j]] = prime[j] * phi[i];
				break;
			}
			phi[i * prime[j]] = phi[prime[j]] * phi[i];
		}
	}
}

int s(int x) {
	if (x <= k) {
		int ans;
		M(ans = qp(2, x) - (k < n ? 1 : 0));
		return ans;
	}
	return dp[x];
}	

void solve() {
	n = _(), k = _();
	f[0][0] = f[1][0] = f[1][1] = g[1][0] = 1;
	rep (i, 2, n + 1) {
		f[i][0] = g[i][0] = 0;
		rep (j, 0, std::min(i, k + 1)) M(f[i][0] += f[i - 1][j]);
		rep (j, 1, std::min(i + 1, k + 1)) f[i][j] = f[i - 1][j - 1];
		rep (j, 0, std::min(i - 1, k + 1)) M(g[i][0] += g[i - 1][j]);
		rep (j, 1, std::min(i, k + 1)) g[i][j] = g[i - 1][j - 1];
	}
	rep (i, 1, n + 1) {
		dp[i] = 0;
		rep (j, 0, std::min(i + 1, k + 1)) M(dp[i] += f[i][j]);
		rep (j, k + 1, std::min(i, 2 * k + 1)) M(dp[i] -= 1LL * (2 * k - j + 1) * g[i - j][0] % MOD);
	}
	int ans = 0;
	for (int i = 1; i * i <= n; i++) {
		if (n % i) continue;
		M(ans += 1LL * s(i) * phi[n / i] % MOD);
		if (i * i == n) continue;
		M(ans += 1LL * s(n / i) * phi[i] % MOD);
	}
	ans = 1LL * ans * qp(n) % MOD;
	printf("%d\n", ans);
}

int main() {
#ifdef LOCAL
	freopen("ans.out", "w", stdout);
#endif
	init();
	int T = _();
	while (T--) solve();
#ifdef LOCAL
	printf("%.10f\n", (db)clock() / CLOCKS_PER_SEC);
#endif
	return 0;
}
posted @ 2020-03-06 16:23  Mrzdtz220  阅读(139)  评论(0)    收藏  举报