Codeforces Round 947 (Div. 1 + Div. 2)
中立的。感觉确实打少了就唐了。
A
卡了 \(8\) min 才过,乐。你考虑 \(x+y\rightarrow y+x\) 的操作其实就是把开头的一段一道末尾。如果操作大于一次,可以合并成一个操作。因此我们最多操作一次。直接复制两边判即可。
B
首先,最小的 \(a_x\) 的 \(x\) 一定要选,否则后面无法补偿。因此我们已经确定了一个了。那么,我们把 \(\mod a_x=0\) 的全部去掉,最小的就是另一个答案。最后再判断一次。
C
wa 了 \(2\) 次,唐。你考虑到如果有两个相同的连续元素了,我们一定可以把这个序列全部变为这个元素。怎么出现两个相同元素呢?容易发现操作 \(>3\) 的长度的区间是不优的,一定可以拆成更多的小区间。所以只需要判断 \(2,3\) 长度的即可。
D
我们先考虑一下 A 和 B 可以集合到一个点的情况。这种情况我们设集合在 \(x\),我们造作次数就是 \(\max(dis(a,x),dis(b,x))+cal(x)\),\(cal(x)\) 的值是以 \(x\) 为根的树要遍历到每一个节点的最小造作步数,贪心容易发现是 \(2(n-1)-maxdep\),\(maxdep\) 是以 \(x\) 为根的最大深度。怎么选集合点呢?我们发现,我们集合点 \(x\) 移动一步,\(maxdep\) 最多 \(+1\),所以我们让 \(\max(dis(a,x),dis(b,x))\) 最小最好,因为移动不会更优。这个就是 \(a,b\) 路径的中点。
如果不能集合到一个点,即 \(a,b\) 路径长度为偶数个点。这个我们就移到它们刚刚距离为 \(1\) 即可,以 \(a\) 作为集合点,最后答案 \(+1\)。
E
这下真的唐掉了。大家就看一个乐子。因为我的做法显然复杂度/程序长度上都不优。
考虑黑色节点形成一条链的条件。
-
记自己是黑色的并且没有黑色的儿子的点的个数为 \(cnt\)。\(cnt\) 显然合法,\(cnt>2\) 显然不合法。因此我们 \(cnt=2\),记这两个点为 \(x,y\)。
-
\(x\) 到 \(y\) 的路径上都是黑色,并且总的黑色数目等于路径上的数目。
要维护单点修改和查询路径和。显然可以用树剖。因此我们得到了 \(\mathcal{O}(n\log^2 n)\) 唐氏做法。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e5+5;
ll n,m,w[N];
ll dfn[N],tot,top[N],fa[N],son[N],dep[N],siz[N],rnk[N],ff[N][22];
vector<int> g[N];
ll dat[N<<2],tag[N<<2];
void init(){
tot=0;
for (int i=1; i<=n; i++){
dfn[i]=top[i]=fa[i]=son[i]=dep[i]=siz[i]=rnk[i]=0;
g[i].clear();
for (int j=0; j<22; j++){
ff[i][j]=0;
}
}
for (int i=1; i<=4*n+4; i++){
dat[i]=tag[i]=0;
}
}
void pu(int k){
dat[k]=dat[k<<1]+dat[k<<1|1];
}
void pd(int k,ll l,ll r){
if (tag[k]){
ll mid=l+r>>1;
tag[k<<1]=tag[k<<1]+tag[k];
tag[k<<1|1]=tag[k<<1|1]+tag[k];
dat[k<<1]=dat[k<<1]+(mid-l+1)*tag[k];
dat[k<<1|1]=dat[k<<1|1]+(r-mid)*tag[k];
tag[k]=0;
}
}
void bd(int k,int l,int r){
tag[k]=0;
if (l==r){
dat[k]=w[rnk[l]];
return;
}
int mid=l+r>>1;
bd(k<<1,l,mid);
bd(k<<1|1,mid+1,r);
pu(k);
}
void upd(int k,int l,int r,int ql,int qr,ll ad){
if (l>qr || r<ql){
return;
}
if (ql<=l && r<=qr){
tag[k]=tag[k]+ad;
dat[k]=dat[k]+ad*(r-l+1);
return;
}
pd(k,l,r);
int mid=l+r>>1;
upd(k<<1,l,mid,ql,qr,ad);
upd(k<<1|1,mid+1,r,ql,qr,ad);
pu(k);
}
ll qy(int k,int l,int r,int ql,int qr){
if (l>qr || r<ql){
return 0;
}
if (ql<=l && r<=qr){
return dat[k];
}
pd(k,l,r);
int mid=l+r>>1;
ll vl=qy(k<<1,l,mid,ql,qr);
ll vr=qy(k<<1|1,mid+1,r,ql,qr);
return vl+vr;
}
void dfs1(int u,int fz){
ll mx=0;
siz[u]=1,fa[u]=fz,dep[u]=dep[fz]+1;
ff[u][0]=fz;
for (int i=1; i<22; i++){
ff[u][i]=ff[ff[u][i-1]][i-1];
}
for (auto v : g[u]){
if (v^fz){
dfs1(v,u),siz[u]+=siz[v];
if (siz[v]>mx){
mx=siz[v],son[u]=v;
}
}
}
}
void dfs2(int u,int tp){
top[u]=tp,dfn[u]=++tot,rnk[tot]=u;
if (!son[u]){
return;
}
dfs2(son[u],tp);
for (auto v : g[u]){
if ((v^fa[u]) && (v^son[u])){
dfs2(v,v);
}
}
}
int lca(int u,int v){
if (dep[u]<dep[v]){
swap(u,v);
}
int dif=dep[u]-dep[v];
for (int i=0; i<20; i++){
if (dif>>i&1){
u=ff[u][i];
}
}
if (u==v){
return u;
}
for (int i=19; i>=0; i--){
if (ff[u][i]!=ff[v][i]){
u=ff[u][i],v=ff[v][i];
}
}
return ff[u][0];
}
void upd_chain(int x,int y,ll z){
while (top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]){
swap(x,y);
}
upd(1,1,n,dfn[top[x]],dfn[x],z);
x=fa[top[x]];
}
if (dep[x]>dep[y]){
swap(x,y);
}
upd(1,1,n,dfn[x],dfn[y],z);
}
ll qy_chain(int x,int y){
ll ans=0;
while (top[x]!=top[y]){
if (dep[top[x]]<dep[top[y]]){
swap(x,y);
}
ans+=qy(1,1,n,dfn[top[x]],dfn[x]);
x=fa[top[x]];
}
if (dep[x]>dep[y]){
swap(x,y);
}
ans+=qy(1,1,n,dfn[x],dfn[y]);
return ans;
}
int snc[N],is[N];
void solve(){
cin>>n>>m;
init();
int cnt11=0;
for (int i=1; i<=n; i++){
snc[i]=is[i]=0;
cin>>w[i];
cnt11+=w[i];
}
for (int i=1; i<n; i++){
int u,v;
cin>>u>>v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(1,0);
dfs2(1,1);
bd(1,1,n);
for (int i=1; i<=n; i++){
if (w[i]){
snc[fa[i]]++;
}
}
int cnt=0;
set<int> st;
for (int i=1; i<=n; i++){
if (w[i] && snc[i]==0){
cnt++;
st.insert(i);
is[i]=1;
}
}
while (m--){
int u;
cin>>u;
upd_chain(u,u,w[u]==1?-1:1);
cnt11+=(w[u]==1?-1:1);
if (!w[u]){
snc[fa[u]]++;
if (is[fa[u]] && fa[u] && snc[fa[u]]==1){
cnt--;
st.erase(fa[u]);
is[fa[u]]=0;
}
if (snc[u]==0){
cnt++;
st.insert(u);
is[u]=1;
}
}
else{
snc[fa[u]]--;
// cout<<fa[u]<<endl;
if (w[fa[u]] && fa[u] && snc[fa[u]]==0){
cnt++;
st.insert(fa[u]);
is[fa[u]]=1;
}
if (is[u]){
cnt--;
st.erase(u);
is[u]=0;
}
}
//cout<<cnt<<"!"<<"\n";
w[u]^=1;
if (cnt==1){
cout<<"Yes\n";
}
else{
if (cnt==0 || cnt>2){
cout<<"No\n";
}
else{
int x=-1,y=-1;
for (auto u : st){
if (x==-1) x=u;
else y=u;
}
//cout<<x<<","<<y<<","<<lca(x,y)<<"\n";
int cal=qy_chain(x,y);
//cout<<cal<<"\n";
if (cal==cnt11 && cal==dep[x]+dep[y]-dep[lca(x,y)]*2+1){
cout<<"Yes\n";
}
else{
cout<<"No\n";
}
}
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
solve();
}
return 0;
}
F
赛后补的。
我们一个一个位确定。考虑你已经确定了 \(x\) 位,那么你的限制还有 \(2^{n-x}\) 条。考虑下一位是 \(1\) 还是 \(0\)。设约束 \(c_1,c_2\),\(c_1\) 下一位是 \(1\),\(c_2\) 是 \(0\)。
-
如果放 \(1\),\(c_1\) 可以用的就会减少 \(1\),产生新的约束 \(c=(c_1»1)\And c_2\)。
-
如果放 \(0\),\(c_1\) 可以用的不会减少,产生新的约束 \(c=c_1\And c_2\)。
dfs 即可。时间复杂度 \(\mathcal{O}(n2^n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int n;
int con[22][1<<21];
vector<int> ans;
void dfs(int s,int p){
if (p==n){
if (con[p][0]&1){
ans.push_back(s);
}
return;
}
int ms=(1<<n-p-1);
for (int i=0; i<ms; i++){
con[p+1][i]=con[p][i]&con[p][i|ms];
}
dfs(s<<1,p+1);
for (int i=0; i<ms; i++){
con[p+1][i]=con[p][i]&(con[p][i|ms]>>1);
}
dfs(s<<1|1,p+1);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
con[0][0]=1;
for (int i=1; i<(1<<n); i++){
cin>>con[0][i];
}
dfs(0,0);
cout<<ans.size()<<"\n";
for (auto u : ans){
cout<<u<<"\n";
}
return 0;
}