0515 模擬賽 A.構造題(找規律 B.毒瘤狀壓算容斥係數 C.提答亂搞

A

題目:
在這裏插入圖片描述在這裏插入圖片描述
標算:
在這裏插入圖片描述
在這裏插入圖片描述
另有找規律解法:先把kk轉化成大於等於的子集數,就是用2n1k2^n-1-k替代kk。然後如果k=1k=-1肯定無解。否則我們寫出一個nnn*n的矩陣,形如倒置的楊輝三角。如下圖:
在這裏插入圖片描述
然後直接對於每一行,從左往右掃,如果kk減去當前格子的數後非負,就減去,然後把答案的鄰接矩陣對應位置設爲11,否則直接跳到下一行。然後就完了。

時間複雜度O(n2)O(n^2)。所以給nn開到10001000,然後kk用高精讀入也是可以做的。(

是不是很nb。

CODE

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
LL w[50][50], k;
int ans[50][50], n;
int main () {
	scanf("%d%lld", &n, &k);
	k = (1ll<<n)-1-k;
	if(k < 0) { puts("-1"); return 0; }
	w[n][1] = 1;
	for(int i = n-1; i; --i)
		for(int j = 1; j <= n-i+1; ++j)
			w[i][j] = w[i+1][j] + w[i+1][j-1];
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n-i+1; ++j)
			if(k >= w[i][j]) k -= w[i][j], ans[i][j] = 1;
			else break;
	for(int i = 1; i <= n; ++i)
		for(int j = 1; j <= n; ++j)
			printf("%d%c", ans[i][j], " \n"[j==n]);
}

B

在這裏插入圖片描述
在這裏插入圖片描述
毒瘤。簡單的想法是每個質數獨立算。相當於求一個方程組的解的個數。這個可以完全揹包來算。然後aia_i是無序的,可以規定成有序算最後除以每個相同aia_i的個數的階乘。

但是要求bib_i不相等。所以必須枚舉集合劃分來容斥。m30m\le 30,所以集合劃分方案最多爲bell(n)bell(n)。然後要求一個劃分的容斥係數。可以發現一個劃分的容斥係數只跟裏面每個集合的Σai\Sigma a_i有關。那麼定義本質不同表示每個集合的Σai\Sigma a_i排序後的序列不相同。那麼本質不同的劃分就很少了。對於每一種劃分的本質我們求出在這種本質下有多少劃分的方案這種本質的容斥係數。那麼最後乘起來再乘上前面完全揹包求方程組解數就是答案了。

在這種本質下有多少劃分的方案可以直接dpdp

求容斥係數要考慮一下。一個劃分的容斥係數就是裏面每個集合的容斥係數之積。那麼考慮一個集合,其實容斥係數只與集合大小有關。如果它的大小爲11,容斥係數就是應該是11。如果大小>1>1,那麼必須是它的所有子集合(包括自己)的容斥係數之和爲00,因爲我們不容許有兩個bib_i相等。
假設g[n]g[n]表示大小爲nn的集合的容斥係數。那麼寫出來就是[n==1]=g[n]+i=1n1(n1i1)g[i][ni==1][n==1]=g[n]+\sum_{i=1}^{n-1}\binom{n-1}{i-1}g[i]*[n-i==1]ii相當於是在固定一個11號節點,枚舉11號節點所在子集的大小ii,然後在n1n-1個點選i1i-1個,乘上這個子集合的容斥係數g[i]g[i]。剩下nin-i個點,如果ni>1n-i>1那麼它的子集合係數和一定是00,所以只考慮ni=1n-i=1的情況。

所以f[1]=1f[1]=1,對於n>1,f[n]=f[n1](n1)n>1,f[n]=-f[n-1]*(n-1)。可得出f[n]=(1)i1(n1)!f[n]=(-1)^{i-1}*(n-1)!

那麼這道題就算完了。可以參考一下代碼:

由於卡常所以手寫了一個只有hashhash功能的setset

CODE

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
typedef vector<int> Vec;
typedef long long LL;
const int mod = 1e9 + 7;
const int MAXM = 31;
const int MAXN = 10005;
const LL p = 131;
struct Set {
	pii a[MAXM]; LL hsh;
	Set() { memset(a, 0, sizeof a); hsh = 0; }
	inline void gethash() {
		sort(a, a + 31, greater<pii>()); hsh = 0;
		for(int i = 0; i < 31 && a[i].first; ++i)
			hsh = (hsh * p + a[i].first) * p + a[i].second;
	}
	inline bool operator <(const Set &o)const { return hsh < o.hsh; }
};
map<Set,int>dp[2];
map<Vec,int>cf;
int n, m, fac[MAXM], inv[MAXM], a[MAXM];
void pre(int N) {
	fac[0] = fac[1] = inv[0] = inv[1] = 1;
	for(int i = 2; i <= N; ++i) fac[i] = 1ll * fac[i-1] * i % mod, inv[i] = 1ll * (mod-mod/i) * inv[mod%i] % mod;
	for(int i = 2; i <= N; ++i) inv[i] = 1ll * inv[i-1] * inv[i] % mod;
}
int pr[MAXN], cnt, mi[MAXN]; bool vis[MAXN];
void pre2(int N) {
	for(int i = 2; i <= N; ++i) {
		if(!vis[i]) pr[++cnt] = i;
		for(int j = 1; j <= cnt && pr[j]*i <= N; ++j) {
			vis[pr[j]*i] = 1; if(i % pr[j] == 0) break;
		}
	}
	for(int i = 1, tmp; i <= cnt; ++i){
		for(tmp = N; tmp; tmp /= pr[i]) mi[i] += tmp/pr[i];
	}
}
int f[MAXN];

inline int add(int x, int y) { return x + y >= mod ? x + y - mod : x + y; }
int main () {
	scanf("%d%d", &n, &m); pre(30); pre2(n);
	for(int i = 1; i <= m; ++i) scanf("%d", &a[i]);
	sort(a + 1, a + m + 1);
	bool cur = 0; dp[cur][Set()] = 1;
	Set u; int w;
	for(int i = 1; i <= m; ++i) { //處理相同本質的劃分方案數
		dp[cur^=1].clear();
		for(map<Set,int>::iterator it = dp[cur^1].begin(); it != dp[cur^1].end(); ++it) {
			w = it->second;
			for(int j = 0; j < 31 && (!j || it->first.a[j-1].first); ++j) {
				u = it->first; u.a[j].first += a[i], ++u.a[j].second;
				u.gethash();
				dp[cur][u] = add(dp[cur][u], w);
			}
		}
	}
	for(map<Set,int>::iterator it = dp[cur].begin(); it != dp[cur].end(); ++it) { //計算容斥係數
		u = it->first, w = it->second;
		Vec vc;
		for(int i = 0; i < 31 && u.a[i].first; ++i) {
			vc.push_back(u.a[i].first);
			w = 1ll * w * fac[u.a[i].second-1] * ((u.a[i].second & 1) ? 1 : -1) % mod;
		}
		if(w < 0) w += mod;
		cf[vc] = add(cf[vc], w);
	}
	int ans = 0;
	for(map<Vec,int>::iterator it = cf.begin(); it != cf.end(); ++it) { //算上方程解的個數(完全揹包計算
		Vec vc = it->first; w = it->second;
		memset(f, 0, sizeof f); f[0] = 1;
		for(int i = 0, len = vc.size(); i < len; ++i)
			for(int j = vc[i]; j <= mi[1]; ++j)
				f[j] = add(f[j], f[j-vc[i]]);
		for(int i = 1; i <= cnt; ++i) w = 1ll * w * f[mi[i]] % mod;
		ans = add(ans, w);
	}
	for(int i = 1, j; i <= m; i = j) { //去掉把ai視作有序的影響
		for(j = i; j <= m && a[j] == a[i]; ++j);
		ans = 1ll * ans * inv[j-i] % mod;
	}
	printf("%d\n", ans);
}

C

在這裏插入圖片描述在這裏插入圖片描述
直接算一下大概每個塊的值,然後蛇形填數。加上第一個點手玩或者寫暴力隨機bfs,能得到92~93分的好成績。我只有92。不好放out文件就不放了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章