[考試記錄] 2024.10.7 csp-s模擬賽37

XiaoLe_MC發表於2024-10-08

T1 莓良心

又是這毛病,場上怎麼也想不到正解,然後看了題解恍然大悟。還是太菜。🐷

大概就是,貪心地,找到所有區間最小的 \(L\) 和最大的 \(R\),那麼所有的點都可以被放置到 \(L\)\(R\) 這兩個點上。

那就好辦了,將所有的 \(l\)\(r\) 排序後討論,分兩種情況討論:

  • 如果 \(L\le R\),那麼所有的點都可以被放到這段區間裡,那麼答案就全為 \(0\)。直接 break。
  • 如果 \(R < L\),那麼它們之間的點的數量為 \(n-i - i\),這些點的貢獻為 \((n-2i)(L-R)\),再加上這個區間自己的那一對點即可。所以貢獻是 \((L-R)(n-2i+1)\)
#include<bits/stdc++.h>
using namespace std;
#define int long long
constexpr int N = 3e5 + 5;
int n, L[N], R[N], ans;
int main(){
	freopen("case.in", "r", stdin); freopen("case.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n; for(int i=1; i<=n; ++i) cin>>L[i]>>R[i];
	sort(L+1, L+1+n, [](const int x, const int y){ return x > y; });
	sort(R+1, R+1+n, [](const int x, const int y){ return x < y; });
	for(int i=1; i<=n; ++i){
		if(L[i] <= R[i]) break;
		ans += (L[i] - R[i]) * (n - (i<<1) + 1);
	} return cout<<ans, 0;
}

T2 盡梨了

很巧妙的大思維題。可我沒有思維。

題目很屎,翻譯過來就是讓你構造兩個零一串,分別滿足 \(a_i\) 或者 \(b_j\) 等於 \(c_{i,j}\)

假設第 \(i\) 行為 \(00110\),如果序列 \(b=00101\) 那麼無解,如果序列 \(b=00100\) 那麼 \(a_i=1\),如果序列為 \(b=11111\) 那麼 \(a=0\)。綜上,只要 \(b\) 中有足夠的 \(1\),那麼這些 \(1\) 都必須要踩在序列的 \(1\) 。設這一行的 \(1\) 的數量為 \(tot\), \(b\)\(1\) 的數量為 \(cnt\)。那麼有:

  • \(cnt<tot\)\(a_i=1\)
  • \(cnt>tot\): \(a_i=0\)
  • \(cnt=tot\)\(a_i=?\)

考慮列舉 \(b\)\(1\) 的數量,然後遍歷每一行。如果有 \(cnt=tot\),那麼這些行的 \(a_i\) 是不確定的,可以發現的是,如果這些行不是完全一樣的話,那麼無解。所以答案 \(*2^{行數}\)。我們記 \(le\) 為小於等於 \(cnt\) 的行的並,\(qe\) 為大於等於 \(cnt\) 的行的並。那麼 \(le\) 中的 \(1\)必須被放的\(qe\) 中的 \(1\)可以被放置的。考慮到 \(b\) 一共有 \(i\)\(1\),那麼還剩下 \(i-cnt_{le}\)\(1\) 可以瞎放,而瞎放的位置還剩下 \(cnt_{qe}-cnt_{le}\) 個,貢獻即為 答案 \(*\dbinom{cnt_{qe}-cnt_{le}}{i-cnt_{le}}\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
constexpr int N = 5e3 + 5, M = 998244353;
int n, fac[N], inv[N];
inline int qpow(int a, int k){
	int ans = 1; while(k){
		if(k & 1) ans = (ll)ans * a % M;
		a = (ll)a * a % M; k >>= 1;
	} return ans;
}
inline void init(){
	fac[0] = 1; for(int i=1; i<=n; ++i) fac[i] = (ll)fac[i-1] * i % M;
	inv[n] = qpow(fac[n], M-2); for(int i=n-1; i>=0; --i) inv[i] = (ll)inv[i+1] * (i+1) % M;
}
inline int C(int a, int b){
	if(a < 0 || b < 0 || a < b) return 0;
	return (ll)fac[a] * inv[b] % M * inv[a-b] % M;
}
bitset<N> mp[N], le[N], qe[N];
vector<int> tot[N];
int ans;
int main(){
	freopen("de.in", "r", stdin); freopen("de.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n; init(); 
	for(int i=1; i<=n; ++i){
		cin>>mp[i]; mp[i] <<= 1;
		tot[mp[i].count()].push_back(i);
	}
	for(int i=1; i<=n; ++i){
		le[i] = le[i-1];
		for(int j : tot[i]) le[i] |= mp[j];
	}
	for(int i=1; i<=n; ++i) qe[n+1][i] = 1;
	for(int i=n; i>=1; --i){
		qe[i] = qe[i+1];
		for(int j : tot[i]) qe[i] &= mp[j];
	}
	for(int i=0; i<=n; ++i){
		if((le[i] & qe[i]) == le[i]){
			int cnt1 = le[i].count(), cnt2 = qe[i].count();
			ans = ((ll)ans + (ll)qpow(2, tot[i].size()) * C(cnt2-cnt1, i-cnt1) % M) % M;
		}
	} return cout<<ans, 0;
}

T3 團不過

實力不行,還去拼盡全力求解容斥……

據說容斥可做,但已經不是我力所能及的範圍了。而且 DP 可以高效地將大容斥劃分成小情況考慮,大大減輕思維難度。

\(g[i]\) 表示有 \(i\) 堆石子時的總方案數,\(f[i]\) 表示這 \(i\) 堆石子異或和不為零且互不相同的方案數。那麼答案即為 \(g[n] - f[n]\)

考慮遞推轉移。\(g[i]\) 即為全排列,所以 \(g[i]=g[i-1]*(tot-1)\)\(tot=2^n\)\(f[i]\) 既要保證異或和為零,又要保證相等,所以 \(f[i]=g[i-1]-f[i-1]-(tot-i+1)*(i+1)*f[i-2]\)

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
constexpr int N = 1e7 + 5, M = 1e9 + 7;
int n, tot, f[N], g[N];
inline int qpow(int a, int k){
	int ans = 1; while(k){
		if(k & 1) ans = (ll)ans * a % M;
		a = (ll)a * a % M; k >>= 1;
	} return ans;
}
int main(){
	freopen("yui.in", "r", stdin); freopen("yui.out", "w", stdout);
	scanf("%d", &n); tot = qpow(2, n); g[1] = tot - 1, f[1] = 0; g[0] = f[0] = 1;
	for(int i=2; i<=n; ++i){
		g[i] = ((ll)g[i-1] * (tot - i) % M + M) % M;
		f[i] = (((ll)g[i-1] - (ll)f[i-1] - (ll)(tot-i+1) * (i-1) % M * f[i-2] % M) % M + M) % M;
	} printf("%d", ((g[n] - f[n]) % M + M) % M);
}

T4 七負我

最大團即為最優答案。證明很好證,使用基本不等式即可。

#include<bits/stdc++.h>
using namespace std;
int n, x, m, mnt;
bitset<45> G[45], R[45], P[45], X[45];
inline void Bron_Kerbosch(int d){
	if(P[d].none() && X[d].none()) return void(mnt = max(mnt, (int)R[d].count()));
	int bg = P[d]._Find_first();
	for(int i=P[d]._Find_first(); i<=n; i=P[d]._Find_next(i)){
		if(G[bg][i]) continue;
		R[d+1] = R[d], P[d+1] = P[d], X[d+1] = X[d];
		R[d+1][i] = 1, P[d+1] &= G[i], X[d+1] &= G[i];
		Bron_Kerbosch(d+1);
		P[d][i] = 0, X[d][i] = 1;
	}
}
int main(){
	freopen("nanami.in", "r", stdin); freopen("nanami.out", "w", stdout);
	ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	cin>>n>>m>>x;
	for(int i=1, u, v; i<=m; ++i)
		cin>>u>>v, G[u][v] = G[v][u] = 1;
	for(int i=1; i<=n; ++i) P[0][i] = 1;
	Bron_Kerbosch(0);
	double ans = (1.0 - 1.0 / mnt) / 2 * x * x;
	cout<<fixed<<setprecision(6)<<ans;
}

相關文章