CF1400
2021-02-25
9 min read
CF1400A String Similarity
输出S的所有偶数位置字符
#include<bits/stdc++.h>
using namespace std;
int T, n;
char s1[10000000];
int main() {
scanf("%d", &T);
while(T-- > 0) {
scanf("%d%s", &n, s1);
for(int i = 0; i < 2 * n - 1; i += 2)printf("%c", s1[i]);
puts("");
}
return 0;
}
CF1400B RPG Protagonist
枚举第一个人买什么
然后第二个人尽可能买小的
#include<bits/stdc++.h>
#define int long long
using namespace std;
int T, p, f, cnts, cntw, s, w;
signed main() {
scanf("%lld", &T);
while(T-- > 0) {
scanf("%lld%lld", &p, &f);
scanf("%lld%lld", &cnts, &cntw);
scanf("%lld%lld", &s, &w);
if(s > w)swap(cnts, cntw), swap(s, w);
int ans = 0;
for(int i = 0; i <= cnts; ++i) {
if(i * s > p)break;
int tp = min((p - i * s) / w, cntw);
// printf("First : %d %d\n", i, i + tp);
if((cnts - i) * s >= f) {
// puts("/jk");
ans = max(ans, tp + i + f / s);
// printf("is %d\n", tp + i + f / s);
} else {
tp += min((f - (cnts - i) * s) / w, cntw - tp);
// printf("%d %d?\n", min((f - (cnts - i) * s) / w, cntw - tp), tp + cnts);
tp += cnts;
ans = max(ans, tp);
}
}
printf("%lld\n", ans);
}
return 0;
}
CF1400C Binary String Reconstruction
从小到大扫过去
对于第一个位置看他前面的能不能放1,能放在前面,否则放在后面并判断能不能放
#include<bits/stdc++.h>
const int MAXN = 1e6 + 7;
int T, x, n;
char s[MAXN];
int a[MAXN];
inline int fpd(int i) {
if((i - x > 0 && a[i - x] == 1) || (i + x <= n && a[i + x] == 1))return 0;
return 1;
}
inline int pd(int i) {
if((i + x <= n && s[i + x] != '1') || (i - x > 0 && s[i - x] != '1'))return 0;
return 1;
}
int main() {
scanf("%d", &T);
while(T-- > 0) {
scanf("%s", s + 1);
scanf("%d", &x);
n = strlen(s + 1);
for(int i = 1; i <= n; ++i)a[i] = 0;
bool flg = 1;
for(int i = 1; i <= n; ++i) {
if(s[i] == '1' && fpd(i)) {
if(i - x > 0 && pd(i - x)) {
a[i - x] = 1;
} else if(i + x <= n && pd(i + x)) {
a[i + x] = 1;
} else {
flg = 0;
break;
}
}
}
if(!flg)puts("-1");
else for(int i = 1; i <= n; ++i)printf("%d", a[i]);
puts("");
}
return 0;
}
CF1400D Zigzags
第一遍预处理前i个位置有多少个相等数对
第二遍在线统计表示前i个值为的求和是什么
CF1400E Clear the Multiset
表示区间全部清空的最小代价
第一种转移是把所有的数不断进行1操作剪剪剪到最小值为0,然后区间变成子区间
第二种转移是直接将整个区间使用2操作推平掉
两类转移取min即可
因为我们每次都会减少一个数,相对带来多出一个状态的代价,所以是线性的
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN = 1e5 + 7;
int a[MAXN], n;
inline int solve(int l, int r) {
if(l > r)return 0;
if(l == r) {
if(a[l] == 0)return 0;
return 1;
}
int ans = 0;
int ans1 = 0;
int tmp = 1e9;
for(int i = l; i <= r; ++i) {
if(a[i] < tmp)tmp = a[i];
if(a[i] != 0)ans1++;
}
for(int i = l; i <= r; ++i)
a[i] -= tmp;
ans = tmp;
tmp = l;
for(int i = l; i <= r; ++i) {
if(a[i] == 0) {
ans += solve(tmp, i - 1);
tmp = i + 1;
} else if(i == r) {
ans += solve(tmp, i);
}
}
ans = min(ans, ans1);
return ans;
}
signed main() {
scanf("%lld", &n);
for(int i = 1; i <= n; ++i)
scanf("%lld", &a[i]);
printf("%lld\n", solve(1, n));
return 0;
}
CF1400F x-prime Substrings
李队之前讲过,咕掉
CF1400G Mercenaries
容斥
钦定至少满足其中x个敌对关系
我们只需要选出一些人使得这些人数量满足限制即可
不难发现我们可以很容易求出单点值,就是对于集合大小为x的方案数,假设为所有满足,应该是
然后因为我们钦定了几对人,所以是
这个式子好像没法O(1)求啊/ll
但是最后关键的一步,我们容斥要看值域大小!!
这个c的值域只有,而且只有m种取值
所以就直接做了
#include<bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int P = 998244353;
const int MAXN = 3e5 + 7;
const int MAXM = 45;
int n, m, x[MAXN], y[MAXN], cnt[MAXN], l[MAXN], r[MAXN];
int fac[MAXN], ifac[MAXN], vis[MAXN];
vector<int> v;
int pre[MAXM][MAXN];
inline ll ksm(ll x, ll y) {
ll ans = 1;
while(y) {
if(y & 1)ans = ans * x % P;
x = x * x % P;
y >>= 1;
}
return ans;
}
inline ll add(ll x, ll y) {
x += y;
if(x >= P)x -= P;
return x;
}
inline ll C(ll n, ll m) {
if(n < m)return 0;
if(m < 0)return 0;
return 1ll * fac[n] * ifac[m] % P * ifac[n - m] % P;
}
inline void init() {
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
fac[0] = 1;
for(int i = 1; i <= n; ++i)fac[i] = 1ll * fac[i - 1] * i % P;
ifac[n] = ksm(fac[n], P - 2);
for(int i = n - 1; i >= 1; --i)ifac[i] = 1ll * ifac[i + 1] * (i + 1) % P;
ifac[0] = 1;
for(int j = 0; j <= 2 * m; j++) {
for(int i = 1; i <= n; ++i) {
pre[j][i] = add(pre[j][i - 1], C(cnt[i] - j, i - j));
}
}
return ;
}
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) {
scanf("%d%d", &l[i], &r[i]);
cnt[l[i]]++;
cnt[r[i] + 1]--;
}
for(int i = 1; i <= n; ++i)cnt[i] += cnt[i - 1];
for(int i = 1; i <= m; ++i) {
scanf("%d%d", &x[i], &y[i]);
v.pb(x[i]);
v.pb(y[i]);
}
init();
int mS = (1 << m) - 1;
ll ans = 0;
for(int S = 0; S <= mS; ++S) {
int cnt = 0, lm = 1, rm = n, qc = 0;
for(int i = 1; i <= m; ++i) {
if(S >> (i - 1) & 1) {
vis[x[i]] = vis[y[i]] = 1;
qc++;
lm = max(lm, l[x[i]]);
rm = min(rm, r[x[i]]);
lm = max(lm, l[y[i]]);
rm = min(rm, r[y[i]]);
}
}
for(auto q : v) {
if(vis[q])cnt++;
vis[q] = 0;
}
if(lm > rm)continue;
// printf(" ?? %d %d %d %d %d\n", qc, cnt, lm, rm, pre[cnt][rm] - pre[cnt][lm - 1]);
if(qc & 1)ans = add(ans, P - pre[cnt][rm] + pre[cnt][lm - 1]);
else ans = add(ans, P + pre[cnt][rm] - pre[cnt][lm - 1]);
ans %= P;
}
printf("%lld\n", ans);
return 0;
}
直接WA了两发,第一发是求逆元哪里爆int了,然后第二处是我组合数那下标越界了....然后第二个else有减法我又没有判掉WA了第三发.....