CSP-S模拟14
A. 莓良心
死因答案统计错误,每次删去两个,而我只删一个
发现维护小的上界 \(r\) 和最大的下界 \(l\)
那么所有数都取在 \([r , l]\) 一定最优
所有数的贡献都是 \((l - r)\)
于是就可以继续处理
如果 \(l <= r\) 那么所有数可以取一个值,不会有贡献,直接 \(break\) 即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 300005;
priority_queue<int>ql;
priority_queue<int, vector<int>, greater<int>>qr;
int main(){
int n = read();
for(int i = 1; i <= n; ++i){
ql.push(read());
qr.push(read());
}
ll ans = 0; int l = 1e8, r = 0;
for(int i = 1; i < n; ++i){
l = min(l, ql.top()); ql.pop();
r = max(r, qr.top()); qr.pop();
if(l <= r)break;
ans += 1ll * (l - r) * (n - i * 2 + 1);
}
printf("%lld\n",ans);
return 0;
}
B. 尽梨了
比较奇妙
我们要求 \(a\) 列为 \(1\) 的方案数
设 \(r_i\) 表示某一行的 \(1\) 的个数
如果 \(r_i > a\) 那么该行必然选 \(1\)
如果 \(r_i < a\) 那么该行必然选 \(0\), 因为如果选 \(1\), 那么对应 \(a\) 列选 \(1\) 的位置必然有 \(0\) 存在,状态非法
如果 \(r_i = a\) 的话,那么该行选 \(1 / 0\) 都可以
于是我们可以处理 \(pre_i\) 表示 \(r <= i\) 的 \(1\) 的位置的并
于是我们可以处理 \(nxt_i\) 表示 \(r >= i\) 的 \(0\) 的位置的并
这样,我们每次取到 \(pre\) \(nxt\), 他们分别表示哪些位置一定为 \(1 / 0\)
他们之间不能有交,否则非法
对于 \(r = a\) ,贡献为 \(2^m\) \(m\) 为这样行的个数, 当选择了这样的行时,所有 \(1\) 的位置都确定下来了,故没有额外贡献
但是当所有都取 \(1\) 时, 我们此时只选取了 \(c1 = count(pre_i)\) 个 \(1\) ,而我们确定 \(c2 = count(nxt_i)\) 个位置可以选,所以此时的贡献为 \(C_{c2 - c1}^{i - c1}\)
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 5005;
const int mod = 998244353;
int n, fac[maxn], inv[maxn], p2[maxn], ans;
bitset<maxn>s[maxn], pre[maxn],nxt[maxn];
vector<int>v[maxn];
char c[maxn];
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int C(int n, int m){
if(n < m || m < 0)return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int main(){
n = read();
for(int i = 1; i <= n; ++i){
scanf("%s", c + 1);
for(int j = 1; j <= n; ++j)if(c[j] == '1')s[i].set(j);
v[s[i].count()].push_back(i);
}
inv[0] = fac[0] = 1; for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % mod;
inv[n] = qpow(fac[n], mod - 2); for(int i = n - 1; i > 0; --i)inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
p2[0] = 1; for(int i = 1; i <= n; ++i)p2[i] = (p2[i - 1] + p2[i - 1]) % mod;
for(int i = 1; i <= n; ++i){
pre[i] = pre[i - 1];
for(int x : v[i])pre[i] |= s[x];
}
for(int i = 1; i <= n; ++i)nxt[n + 1].set(i);
for(int i = n; i >= 1; --i){
nxt[i] = nxt[i + 1];
for(int x : v[i])nxt[i] &= s[x];
}
for(int i = 0; i <= n; ++i)if((pre[i] | nxt[i]) == nxt[i]){
ans += p2[v[i].size()] - 1; ans %= mod;
int c1 = pre[i].count(), c2 = nxt[i].count();
ans += C(c2 - c1, i - c1); ans %= mod;
}
printf("%d\n",ans);
return 0;
}
C. 团不过
正难则反,我们求不合法的方案数
设 \(g_i\) 表示 \(i\) 堆石子的总方案数 \(g_i = (2^{n} - 1)^{i\_}\)
不合法异或为 \(0\)
设 \(f_i\) 表示 \(i\) 堆石子不合法的方案数
异或为 \(0\), \(f_i += g_{i - 1} - f_{i - 1}\)
就是 \(i - 1\) 堆合法, 第 \(i\) 堆取他们的异或
但是这样会取重复元素,算多了
所以要减去选重的 \(f_{i - 2} \times (i - 1) \times (2^n - i + 1)\)
某两次选取选重,其他满足非法 \(f_{i - 2}\)
有 \(i - 1\) 个位置可能选重, 选重的有 \(2^n - i + 1\) 种取值
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
const int maxn = 1e7 + 55;
const int mod = 1e9 + 7;
int n;
int f[maxn], g[maxn], pow2;
int qpow(int x, int y){
int ans = 1;
for(; y; y >>= 1, x = 1ll * x * x % mod)if(y & 1)ans = 1ll * ans * x % mod;
return ans;
}
int main(){
n = read();
pow2 = qpow(2, n);
g[0] = 1; for(int i = 1; i <= n; ++i)g[i] = 1ll * g[i - 1] * (pow2 - i) % mod;
for(int i = 3; i <= n; ++i)
f[i] = ((g[i - 1] - f[i - 1] - 1ll * (i - 1) * f[i - 2] % mod * (pow2 - i + 1) % mod) + mod) % mod;
int ans = (g[n] - f[n] + mod) % mod;
printf("%d\n",ans);
return 0;
}
D. 七负我
通过大胆猜测 + 不完全 完全不归纳发现平分一定最优,于是可以有状压暴力了
猜测一定选最大完全子图,于是不会了
解法 \(BK\) 好像就是搜索 + 剪枝,挺离谱
这里使用 \(\text{meet in middle}\)
先跑一半,求出 \(f_s\) 表示集合为 \(s\) 时最大完全子图
求法先跑出完全子图放到对应位置,然后取去掉某个点的 \(f\) 和自己的 \(max\)
跑另外一半,出完全图后,对他们在前一半的边取交集 \(s\), 那么 \(popcount + f_s\) 即为当前最大完全子图
取 \(max\) 即可
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
inline int read(){
int x = 0; char c = getchar();
while(c < '0' || c > '9')c = getchar();
do{x = (x << 3) + (x << 1) + (c ^ 48); c = getchar();}while(c <= '9' && c >= '0');
return x;
}
int n, m, x, cnt[1 << 21 | 1], mx;
ll r[45];
int f[1 << 21 | 1];
int main(){
n = read(), m = read(), x = read();
for(int i = 1; i <= m; ++i){
int u = read(), v = read();
r[u] |= (1ll << (v - 1));
r[v] |= (1ll << (u - 1));
}
int n1 = n >> 1, n2 = n - n1;
int mx1 = 1ll << n1, mx2 = 1ll << n2;
for(int i = 1; i <= mx2; ++i) cnt[i] = cnt[i - (i & -i)] + 1;
for(int i = 1; i < mx1; ++i){
bool fl = 1;
for(int j = 1; j <= n1; ++j)
if(i & (1 << (j - 1)))
if((r[j] & i) != (i xor (1 << (j - 1)))){fl = 0; break;}
if(fl)f[i] = cnt[i];
}
for(int i = 1; i < mx1; ++i)
for(int j = 1; j <= n1; ++j)
if(i & (1 << (j - 1))) f[i] = max(f[i], f[i xor (1 << (j - 1))]);
for(int i = 0; i < mx2; ++i){
ll s = mx1 - 1;
bool fl = 1;
for(int j = 1; j <= n2; ++j)if(i & (1 << (j - 1))){
if(((r[j + n1] >> n1) & i) != (i xor (1 << (j - 1)))){
fl = 0; break;
}
s &= r[j + n1];
}
if(fl)mx = max(mx, f[s] + cnt[i]);
}
double one = (double)x / mx;
double ans = one * one * (mx * (mx - 1) / 2);
printf("%.6lf\n",ans);
return 0;
}