CF1863 题解
CF1863 题解
A
条件很简单:如果总共的 '+' 号加上开始上线人数不到 \(n\) 人,就不可能。实时记录人数,如果某一时刻大于等于 \(n\) 人在线上,就一定是。剩余情况则可能。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,a,q,T;
cin>>T;
while(T--)
{
cin>>n>>a>>q;
int flag = (a >= n),sig = a; char c;
for(int i = 1;i <= q;i++)
{
cin>>c;
if(c == '+') ++sig,++a;
else --a;
if(a == n) flag = 1;
}
if(flag) puts("YES");
else if(sig < n) puts("NO");
else puts("MAYBE");
}
return 0;
}
B
我们发现,如果 \(1,2,3,\dots,k\) 的位置是单增的,一次操作就可以将它们挪到前面,如果 \(k + 1\) 夹在它们中间,取 \(k\) 以外的所有值都无法分开它们,所以答案就是\(\sum_{val = 2}^n [pos_{val} < pos_{val - 1}]\) 。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,a[N],p[N];
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i = 1;i <= n;i++) cin>>p[i],a[p[i]] = i;
int ans = 0;
for(int i = 2;i <= n;i++) if(a[i] < a[i - 1]) ++ans;
cout<<ans<<endl;
}
return 0;
}
C
容易发现一开始序列中缺少了一个数,这个数在第二次一定会出现。第一个数被替换后,整个序列的 MEX 就变成了第一个数,于是第二个数又被替换成第一个数...所以相当于在序列末尾增加了一个数,每操作一次相当于将起点向左移了一个,最后从起点开始向右移 \(k\) 次即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int n,k,a[N],vis[N];
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>k;
for(int i = 1;i <= n;i++) cin>>a[i];
fill(vis + 0,vis + n + 1,0);
for(int i = 1;i <= n;i++) vis[a[i]] = 1;
for(int i = 0,orn = n;i <= orn;i++) if(!vis[i]) a[++n] = i;
k %= n;
if(k == 0) for(int i = 1;i <= n - 1;i++) cout<<a[i]<<" ";
else
for(int i = n - k + 1,c = 1;c <= n - 1;i++,c++)
{
if(i == n + 1) i = 1;
cout<<a[i]<<" ";
}
cout<<endl;
}
return 0;
}
D
第一眼看出各种奇怪思路,然后仔细发现一个性质:每一行和每一列要填多少白格是定的,如果每行/列要填的格子是奇数,就无解。对于一行,横着的骨牌一定会一黑一白,所以其实不影响,所以只有竖着的骨牌影响。
推到这里我们发现此题行列不制约,所以对行和列单独做,贪心染白色即可,如果染不下去就无解。
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5,M = 505;
char s[M][M],cor[M][M];
vector <int> c[M],d[M];
int n,m,cnt = 0,numc[M],numd[M];
inline bool solverow()
{
for(int i = 1;i <= n;i++)
{
if(numc[i] & 1) return false;
numc[i] /= 2;
}
for(int i = 1;i <= n - 1;i++)
{
for(vector <int> :: iterator it = c[i].begin();it != c[i].end();it++)
{
int now = (*it);
if(!numc[i])
{
if(!numc[i + 1]) return false;
else numc[i + 1]--,cor[i][now] = 'B',cor[i + 1][now] = 'W';
}
else
{
cor[i][now] = 'W'; cor[i + 1][now] = 'B';
numc[i]--;
}
}
}
return true;
}
inline bool solvecolumn()
{
for(int i = 1;i <= m;i++)
{
if(numd[i] & 1) return false;
numd[i] /= 2;
}
for(int i = 1;i <= m - 1;i++)
{
for(vector <int> :: iterator it = d[i].begin();it != d[i].end();it++)
{
int now = (*it);
if(!numd[i])
{
if(!numd[i + 1]) return false;
else numd[i + 1]--,cor[now][i] = 'B',cor[now][i + 1] = 'W';
}
else
{
cor[now][i] = 'W'; cor[now][i + 1] = 'B';
numd[i]--;
}
}
}
return true;
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m;
for(int i = 1;i <= n;i++) numc[i] = 0,c[i].clear();
for(int i = 1;i <= m;i++) numd[i] = 0,d[i].clear();
for(int i = 1;i <= n;i++)
for(int j = 1;j <= m;j++)
{
cin>>s[i][j];
if(s[i][j] == 'U') c[i].push_back(j),++numc[i],++numc[i + 1];
if(s[i][j] == 'L') d[j].push_back(i),++numd[j],++numd[j + 1];
}
if(!solverow()) {puts("-1"); continue;}
if(!solvecolumn()) {puts("-1"); continue;}
for(int i = 1;i <= n;i++,cout<<endl)
for(int j = 1;j <= m;j++)
{
if(s[i][j] == '.') cor[i][j] = '.';
cout<<cor[i][j];
}
}
return 0;
}
E
按照拓扑序推一遍,得到一个答案,我们发现我们可以将开始的几个推到第二天去,虽然结束时间可能增多,但是开始时间变晚,可能答案更优。我们又发现这个最多只会推一天,不然没意思。
由于开始的几个(入度为 \(0\) 的几个)最多只向后推一天,所以每一个事件都只可能向后延一天,将开始点按照 \(h_i\) 排序,所以每次更改后搜索,记录这个事件是否向后推过,如果推过或者本来就不用推,就停止搜索。这样复杂度是 \(\Theta(n)\) 的(不算对开始点的排序)。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
vector <int> G[N];
int k,m,deg[N],a[N],n,cnt = 0,line[N],vis[N];
typedef long long ll;
ll t[N],ans = 0,maxn = 0;
inline bool cmp(int x,int y){return a[x] < a[y];}
inline void dfs(int x,int last)
{
maxn = max(maxn,t[x]);
for(auto to : G[x])
{
if(vis[to]) continue;
if(t[to] < t[x]) {t[to] += k; vis[to] = 1; dfs(to,x);}
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n>>m>>k;
cnt = 0;
for(int i = 1;i <= n;i++) cin>>a[i],G[i].clear(),deg[i] = 0;
fill(t + 1,t + n + 1,0);
fill(vis + 1,vis + n + 1,0);
for(int i = 1,x,y;i <= m;i++)
{
cin>>x>>y;
G[x].push_back(y); ++deg[y];
}
queue <int> q;
for(int i = 1;i <= n;i++) if(!deg[i]) line[++cnt] = i,q.push(i),t[i] = a[i];
sort(line + 1,line + cnt + 1,cmp);
while(!q.empty())
{
int x = q.front();
q.pop();
for(auto to : G[x])
{
if(a[to] >= a[x]) t[to] = max(t[to],t[x] + a[to] - a[x]);
else t[to] = max(t[to],t[x] + a[to] + k - a[x]);
--deg[to];
if(!deg[to]) q.push(to);
}
}
maxn = 0;
for(int i = 1;i <= n;i++) maxn = max(maxn,t[i]);
ans = maxn - t[line[1]];
for(int i = 1;i <= cnt - 1;i++)
{
t[line[i]] += k;
dfs(line[i],0);
ans = min(ans,maxn - t[line[i + 1]]);
}
cout<<ans<<endl;
}
return 0;
}
F
考虑 \(\Theta(n^3)\ dp\) ,设 \(f_{l,r}\) 表示 \([l,r]\) 能否被表示出来,每次枚举断点 \(k\) ,更新 \(f_{l,k},f_{k + 1,r}\) 。
再考虑如果 \(f_{l,r}\) 能在 \(k\) 处分开,有什么性质: 假设第 \(p\) 位是 \(sum_{l,r}\) 的 \(highbit\) ,那么 \(p\) 一定是 \(f_{l,k}\) 和 \(f_{k + 1,r}\) 第一个不一样的地方,此时谁取 \(1\) 谁就更大。所以我们记 \(L_i\) 为左端点在 \(i\) 的区间,并且能被表示出的区间中 \(highbit\) 的集合,\(R_i\) 为右端点,同理。按照长度从大到小排序,所以当前区间转移时,可以用的区间一定包含当前区间,所以 \(sum_{i,j} \& L_i > 0\) 或者 \(sum_{i,j} \& R_i > 0\) 是\([i,j]\) 可以转移到的充要条件,按此转移即可。
注意 \(sum_{i,j} = highbit = 0\) 时也可以转移,所以记录一个 “广义零值” 来区分“没有转移” 和 “转移为零“。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e4 + 5;
typedef long long ll;
const ll ZERO = 1ll << 61;
ll a[N],sum[N],L[N],R[N];
int n,ans[N];
inline ll highbit(ll x) {return (x == ZERO) ? ZERO : (1ll << (63 - __builtin_clzll(x)));}
inline ll getsum(int l,int r) {return (sum[r] ^ sum[l - 1]) ? (sum[r] ^ sum[l - 1]) : ZERO;}
int main()
{
int T;
cin>>T;
while(T--)
{
cin>>n;
for(int i = 1;i <= n;i++) cin>>a[i];
sum[0] = 0;
for(int i = 1;i <= n;i++) sum[i] = sum[i - 1] ^ a[i];
fill(L + 1,L + n + 1,0); fill(R + 1,R + n + 1,0);
L[1] = highbit(getsum(1,n)); R[n] = highbit(getsum(1,n)); ans[1] = 1;
for(int len = n - 1;len >= 1;len--)
{
for(int i = 1;i + len - 1 <= n;i++)
{
int j = i + len - 1;
ll jdg = ((getsum(i,j) | ZERO) & (L[i] | R[j]));
if(jdg) L[i] |= highbit(getsum(i,j)),R[j] |= highbit(getsum(i,j));
if(len == 1) ans[i] = (jdg > 0);
}
}
for(int i = 1;i <= n;i++) cout<<ans[i];
cout<<endl;
}
return 0;
}
G
咕。
H
考虑 \(dp\) 方程: \(f_x = 2\max\{f_{lc},f_{rc}\} - [dp_{lc} \neq dp_{rc}]\) 。因为每次落下来的饼干是均匀分给左右的,所以次数是两倍的两边最大值,如果不一样则先给大的那一个,最后就可以少走一次。
记 \(d_x = dp_x - 1\) ,则 \(d_x = 2\max\{d_{lc},d_{rc}\} + [d_{lc} == d_{rc}]\) ,记 \(dep_x\) 为深度, \(D_x = d_x\times 2^{dep_x}\) 。得到 \(D_x = \max\{D_{lc},D_{rc}\} + 2^{dep_x}[D_{lc} == D_{rc}]\)。
最后答案就是 \(1 + \max{D_x}\) ,但是不可能用一个数存下 \(D\) ,考虑 \(D\) 一路向上,每次最多附有一个 \(1\) ,最多有 \(\log a_i + \log n\) 个 \(1\) ,因为单个点可以有 \(\log a_i\) 个 \(1\) ,每次合并增加 \(1\) 需要两个相同的值。
用一个集合存储 \(D\) ,存其中二进制为 \(1\) 的个数,我们发现 \(1\) 是一个个向上推的,所以 \(u\) 和 \(v\) 的 \(D\) 相同,则会在 \(lca\) 处贡献一个 \(D + 2^{dep_{lca}}\) 。一开始将叶子初始化为 \((a_v - 1) \times 2^{dep_v}\) 。每加入一个点往上更新 \(lca\) 即可,由于每次增加一个 \(1\) ,所以不会超过 \(\log\) 次。,每次修改的时候,删除原数的贡献,再增加新数的贡献即可,复杂度 \(\Theta(n\log^2 n)\) 。
#include<bits/stdc++.h>
#define int long long
using namespace std;
#define ll long long
#define vec vector<int>
const int N = 2e5 + 5,MOD = 998244353,inf = 1e18;
vec G[N];
int cnt = 0,h[N],rev[N],dep[N],n,q,dfn[N],fa[22][N],lg[N];
ll pw[N];
set <pair<vec,int> > s;
inline int maxdep(int x,int y) {return dep[x] > dep[y] ? x : y;}
inline int mindep(int x,int y) {return dep[x] < dep[y] ? x : y;}
inline void dfs(int x,int last)
{
dfn[x] = ++cnt;
rev[cnt] = x;
fa[0][dfn[x]] = last;
dep[x] = dep[last] + 1;
for(auto to : G[x])
dfs(to,x);
}
inline int lca(int x,int y)
{
if(x == y) return x;
x = dfn[x]; y = dfn[y];
if(x > y) swap(x,y);
int nlg = lg[y - x + 1];
return mindep(fa[nlg][x],fa[nlg][y - pw[nlg] + 1]);
}
inline int nxt(auto it)
{
auto id = next(it),ip = prev(it);
int ret = 0;
if((*id).first == (*it).first) ret = maxdep(ret,lca((*it).second,(*id).second));
if((*ip).first == (*it).first) ret = maxdep(ret,lca((*it).second,(*ip).second));
return ret;
}
inline void ist(int x)
{
if(!h[x]) return;
vec now;
for(int i = 30;i >= 0;i--) if(((h[x] - 1) >> i) & 1) now.push_back(dep[x] + i);
auto it = s.insert(make_pair(now,x)).first;
while(it != s.end())
{
int nc = nxt(it);
if(!nc) break;
now.push_back(dep[nc]);
it = s.insert(make_pair(now,nc)).first;
}
}
inline void del(int x)
{
if(!h[x]) return;
vec now;
for(int i = 30;i >= 0;i--) if(((h[x] - 1) >> i) & 1) now.push_back(dep[x] + i);
auto it = s.find(make_pair(now,x));
while(it != s.end())
{
int q = nxt(it);
s.erase(it);
if(!q) break;
now.push_back(dep[q]);
it = s.find(make_pair(now,q));
}
}
inline ll Query()
{
if(s.size() == 2) return 0;
ll res = 1;
auto it = s.end(); it--; it--;
for(auto in : (*it).first) res += pw[in],res %= MOD;
return res;
}
signed main()
{
cin>>n;
s.insert(make_pair(vec{inf},0)); s.insert(make_pair(vec{-1},0));
pw[0] = 1;
for(int i = 1;i <= N - 1;i++) pw[i] = pw[i - 1] * 2 % MOD;
lg[0] = -1;
for(int i = 1;i <= N - 1;i++) lg[i] = lg[i >> 1] + 1;
for(int i = 2,x;i <= n;i++) cin>>x,G[x].push_back(i);
for(int i = 1;i <= n;i++) cin>>h[i];
dep[0] = -1; dep[n + 1] = inf;
dfs(1,0);
for(int i = 1;i <= 20;i++)
for(int j = 1;j + pw[i] - 1 <= n;j++)
fa[i][j] = mindep(fa[i - 1][j],fa[i - 1][j + pw[i - 1]]);
for(int i = 1;i <= n;i++) ist(i);
cout<<Query()<<endl;
cin>>q;
for(int i = 1,x,v;i <= q;i++)
{
cin>>x>>v;
del(x);
h[x] = v;
ist(x);
cout<<Query()<<endl;
}
return 0;
}
I
咕。