【22暑期复建1】 Codeforces Round #791 (Div. 2)
传送门Codeforces Round #791 (Div. 2)
真的好久不写cf,一些疯狂踩坑
A - AvtoBus
prob.:
有很多车,有些车是4个轮子,有些是6个轮子,一共有n个轮子,问最少可能有几辆车,最多可能有几辆车?
idea:
分类讨论
注意判断无解的情况
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
signed main() {
// cerr << (998244353998244352 / 2 - 3) / 2 << endl;
ll _;
cin >> _;
while (_--) {
ll n;
cin >> n;
if (n & 1 || n < 4) {
cout << -1 << endl;
continue;
}
if ((n / 2) & 1) {
ll tmp = n / 6;
if (tmp & 1) {
} else {
tmp--;
}
ll a = (n / 2 - tmp * 3) / 2;
ll b = (n - a * 4) / 6;
cout << a + b << " " << " " << (n / 2 - 3) / 2 + 1<< endl;
} else {
ll tmp = n / 6;
if (!(tmp & 1)) {}
else {
tmp--;
}
ll a = (n / 2 - tmp * 3) / 2;
ll b = (n - a * 4) / 6;
cout << a + b << " " << (n / 4) << endl;
}
}
return 0;
}
B - Stone Age Problem
prob.:
n个数,两种操作,1-把第i个数换成x,2-把所有数都换成x,问每次操作后n个数的和是多少
idea:
线段树肯定是能做的,但这才B题
存一个当前所有数的相同时的值pre,(即操作2的x值)
对于次操作用map存一下把当前位置的数变成了什么,计算和的时候map里面存的值相当于是在上次2操作的基础上改了的位置,且每个位置只存了最后一次改动的值
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
map<ll, ll> mp;
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll n, q;
cin >> n >> q;
ll sum = 0;
ll pre = -1;
for(ll i = 1;i <= n; ++ i) {
ll x;
cin >> x;
mp[i] = x;
sum += x;
}
while(q--) {
ll op ;
cin >> op;
if(op == 1) {
ll pos, x;
cin >> pos >> x;
if(mp.find(pos) != mp.end()) {
sum -= mp[pos];
}
else {
sum-=pre;
}
sum += x;
cout << sum << endl;
mp[pos] = x;
}
else {
ll x;
cin >> x;
pre = x;
sum = x * n;
mp.clear();
cout << sum << endl;
}
}
return 0;
}
C - Rooks Defenders
prob. :
有一个 n × n n \times n n×n的棋盘,有三种操作,1-在 ( x , y ) (x, y) (x,y)上放一个Rock,它会影响到这个与它x轴或y轴坐标的所有点(标记?),2-拿走 ( x , y ) (x, y) (x,y)上的一个Rock,保证不会有无效操作,3-问一个矩阵范围内的所有点是否都被标记
idea:
x轴和y轴分开考虑,每种全部标记的情况要不就是x轴完全标记要不就是y轴完全标记,可以自己画小样例理解一下
以x轴为例,要达成一段区间内的完整标记,考虑通过树状数组来完成,(树状数组区间和等于区间长度则为完整标记)
注意当某一个位置有多个值的时候,这个位置只考虑一次,所以加一个cnt数组辅助,当第一个加的时候和最后一个改的时候才对树状数组作更改
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5 + 10;
int cntX[N], cntY[N];
ll treeRow[N << 2], treeCol[N << 2];
ll n;
inline ll lowbit(ll x) {
return x & (-x);
}
void updateRow(ll i, ll x) {
while (i <= n) {
treeRow[i] += x;
i += lowbit(i);
}
}
ll queryRow(ll i) {
ll res = 0;
while (i) {
res += treeRow[i];
i -= lowbit(i);
}
return res;
}
void updateCol(ll i, ll x) {
while (i <= n) {
treeCol[i] += x;
i += lowbit(i);
}
}
ll queryCol(ll i) {
ll res = 0;
while (i) {
res += treeCol[i];
i -= lowbit(i);
}
return res;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
ll q;
cin >> n >> q;
while (q--) {
ll op;
cin >> op;
if (op == 1) {
ll x, y;
cin >> x >> y;
cntX[x]++;
cntY[y]++;
if (cntX[x] == 1) updateRow(x, 1);
if (cntY[y] == 1) updateCol(y, 1);
} else if (op == 2) {
ll x, y;
cin >> x >> y;
cntX[x]--;
cntY[y]--;
if (cntX[x] == 0) updateRow(x, -1);
if (cntY[y] == 0) updateCol(y, -1);
} else {
ll x1, x2, y1, y2;
cin >> x1 >> y1 >> x2 >> y2;
bool flag = 0;
if (queryRow(x2) - queryRow(x1 - 1) == x2 - x1 + 1) flag = 1;
if (queryCol(y2) - queryCol(y1 - 1) == y2 - y1 + 1) flag = 1;
if (flag) cout << "Yes\n";
else cout << "No\n";
}
}
return 0;
}
D - Toss a Coin to Your Graph…
prob.:
给一个有向图,每个点有一个点权,问长度超过k的路径中最大的点权最小值是多少
idea:
二分
二分最大点权值建图,然后对于每个图里面判有没有环,有环就可行,否则拓扑序找最长路径看是否大于k
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
int a[N];
int n, m;
ll k;
struct edge {
int from, to;
};
edge edges[N];
vector<int> g[N];
bool vis[N], instack[N];
int val[N], din[N];
bool haveLoop = false;
void dfs(ll x) {
vis[x] = instack[x] = true;
for (auto to : g[x]) {
if (!vis[to]) dfs(to);
else if (instack[to]) {
haveLoop = true;
}
}
instack[x] = false;
}
bool check(int mid) {
for (int i = 0; i < N; ++i) {
g[i].clear();
vis[i] = false;
din[i] = 0;
val[i] = -1;
instack[i] = 0;
}
haveLoop = false;
for (int i = 1; i <= m; ++i) {
int from = edges[i].from;
int to = edges[i].to;
if (a[from] <= mid && a[to] <= mid) {
g[from].push_back(to);
din[to]++;
}
}
// find cir ,if have -> true
for(int i = 1;i <= n; ++ i) {
if(!vis[i]) dfs(i);
}
// dfs(0);
if (haveLoop) return true;
// the longest path >= k?
for (int i = 1; i <= n; ++i) {
if (din[i] == 0) {
g[0].push_back(i);
din[i]++;
}
}
queue<int> que;
que.push(0);
val[0] = 0;
int res = -1;
while (!que.empty()) {
int tmp = que.front();
que.pop();
for (auto to : g[tmp]) {
din[to]--;
val[to] = max(val[to], val[tmp] + 1);
res = max(res, val[to]);
if (din[to] == 0) que.push(to);
}
}
return (res >= k);
}
signed main() {
// cin >> n >> m >> k;
scanf("%d%d%lld", &n, &m, &k);
int mn = 0x3f3f3f3f;
for (ll i = 1; i <= n; ++i) {
cin >> a[i];
mn = min(mn, a[i]);
}
if (k == 1ll) {
printf("%d", mn);
// cout << mn << endl;
return 0;
}
for (int i = 1; i <= m; ++i) {
// cin >> edges[i].from >> edges[i].to;
scanf("%d%d", &edges[i].from, &edges[i].to);
}
int l = 0, r = 1e9 + 10, ans = -1;
while (l <= r) {
int mid = (l + r) >> 1;
if (check(mid)) {
ans = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
printf("%d", ans);
// cout << ans ;
return 0;
}
E - Typical Party in Dorm
prob.:
给一个由前17个小写字母组成的string s,同时含有一些"?“, 若干次询问,每次给出一个字母集合,可以由这些字母填充”?",问回文串的总个数
idea:
对于每个子串作为回文串时考虑有多少"?“是能随意填的,包括子串内部的对称的”?“和子串外部的所有”?"
对于每个子串考虑它至少需要的字母集合,即以当前子串为回文串时有些“?”是固定的,它们会用到一些字母
在至少的字母集合上考虑sosdp(结合数据范围可以想到)
刚开始想的是维护每个子集可以随意填的“?”的个数然后随着sosdp更新,这样的转移长度极端条件是500,必炸
对于每个子串于最小字母子集,直接将子串对于 包括当前最小字母子集 且长度为len的字母集合 的贡献算出来,统一转移,这样的转移长度是log的(即17)
第一次自己做出2400的题(*冲冲冲冲
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
//map<pair<int, int>, ll> dp;
ll dp[(1 << 17) + 10][20];
ll qpow(ll x, ll n) {
ll res = 1;
while (n) {
if (n & 1) res = res * x % mod;
x = x * x % mod;
n >>= 1;
}
return res;
}
ll getCnt(ll x) {
int res = 0;
while (x) {
if (x & 1)res++;
x >>= 1;
}
return res;
}
signed main() {
ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int n;
cin >> n;
string s;
cin >> s;
int tot = 0;
for (auto ch : s) {
if (ch == '?') tot++;
}
for (int left = 0; left < n; ++left) {
int ll = left, rr = left;
int tmpPos = 0;
int cnt = 0;
int num = 0;
while (1) {
// cerr <<"lll rr" <<ll << " " <<rr << endl;
if (ll < 0 || rr >= n) break;
if (s[ll] == '?' && s[rr] == '?') {
cnt++;
num += 2;
if (ll == rr) {
num--;
}
} else if (s[ll] == '?' || s[rr] == '?') {
char ch = s[ll] == '?' ? s[rr] : s[ll];
tmpPos |= (1 << (ch - 'a'));
num++;
} else if (s[ll] != s[rr]) {
break;
} else {
// continue;
}
int count = getCnt(tmpPos);
for (int len = count; len <= 17; ++len) {
dp[tmpPos][len] = (dp[tmpPos][len] + qpow(len, tot - num + cnt)) % mod;
// dp[{tmpPos, len}] = (dp[{tmpPos, len}] + qpow(len, tot - num + cnt)) % mod;
}
ll--;
rr++;
}
ll = left, rr = left + 1;
tmpPos = 0;
cnt = 0;
num = 0;
while (1) {
// cerr <<"lll rr" <<ll << " " <<rr << endl;
if (ll < 0 || rr >= n) break;
if (s[ll] == '?' && s[rr] == '?') {
cnt++;
num += 2;
if (ll == rr) num--;
} else if (s[ll] == '?' || s[rr] == '?') {
char ch = s[ll] == '?' ? s[rr] : s[ll];
tmpPos |= 1 << (ch - 'a');
num++;
} else if (s[ll] != s[rr]) {
break;
} else {
// continue;
}
int count = getCnt(tmpPos);
for (int len = count; len <= 17; ++len) {
dp[tmpPos][len] = (dp[tmpPos][len] + qpow(len, tot - num + cnt)) % mod;
// dp[{tmpPos, len}] = (dp[{tmpPos, len}] + qpow(len, tot - num + cnt)) % mod;
}
ll--;
rr++;
}
}
int nnn = 17;
for (int i = 0; i < nnn; ++i)
for (int mask = 0; mask < (1 << nnn); ++mask) {
if (mask & (1 << i)) {
for (int len = 0; len <= nnn; ++len) {
dp[mask][len] = (dp[mask][len] + dp[mask ^ (1 << i)][len]) % mod;
// dp[{mask, len}] = (dp[{mask, len}] + dp[{mask ^ (1 << i), len}]) % mod;
}
}
}
// for(int mask = 0 ; mask < ( 1<< nnn); ++ mask) {
// for(int len = 0; len <= nnn; ++ len) {
// cout << dp[{mask, len}] << " ";
// }
// cout << endl;
// }
int q;
cin >> q;
while (q--) {
string t;
cin >> t;
int tmpPos = 0;
for (auto ch : t) {
tmpPos |= (1 << (ch - 'a'));
}
// cout << dp[{tmpPos, t.size()}] << endl;
cout << dp[tmpPos][t.size()] << endl;
}
}