2022 牛客多校第六场 & 第八场题解
2022 牛客多校第六场 & 第八场题解
Forest
依照 Kruskal,对每条边计算让两个点不连通的方案数。容斥+精密实现即可通过。
Fourier and Theory for the Universe
一眼 min_25,然后没写。。。后来分析一通,发现确实 min_25 用 dfs 实现的时候将这些东西都记录下来了。。。
From AtCoder
若存在一个 n 阶排列的和 \(<0\),那么一定没解。否则可以通过以下构造得到答案:
假设主对角线是最大匹配的排列,那么执行 \((n,i,a(i,i))\),使得 \(a(i,i)=0(1\le i<n),a(n,n)\ge 0\)
之后便是约束 \(x_i-x_j+a(i,j)\ge 0\),直接上差分约束即可。
这里先处理 \(a(i,i)\) (应该是?)差分约束无法处理这些点。
那么问题就变成求这个排列了,直接 KM 即可。(昨天去学了一波,挺妙的算法)
Hash
乱搞,分组,尽量将能同余的数量变大。注意数据随机。
Line
直接暴力将所有的点在该方向放 \(d-1\) 个点即可。注意不能共线,可以找几个大质数为 \(base\) 多随几次,或者直接上 \(31\) 或 \(37\)。(玄学)
SolarPea and Inversion
Striking String Problem
这场是人做的吗.jpg
Lexicographic Comparison
队友和我一起想,我写的题。
首先,\(A\) 数组其实没有太大用,你只需要判断两个序列第一个不一样在哪个位置,再把 \(A\) 比较一下即可。
一次变换相当于在一个循环中移位,发现当循环节长度 \(\%\ (y-x)=0\) 就会相等。那么只需找到循环节长度模后不等于 \(0\) 的第一个位置。
那么就可以进行分类了:
若循环节长度 \(\le 64\),我们对所有这样长度的都开一棵平衡树,维护位置的最小值。
若循环节长度 \(>64\),开个 \(set\) 维护一下,直接暴力询问所有的循环节即可。
现在有个 \(P\) 数组的 swap 操作,发现是一个开环和缩环的过程,直接拿非旋 \(treap\ split\ merge\) 一下即可。代码细节有亿点点多,好久没写过这么长的代码了
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100005;
int n,m,B,a[maxn],p[maxn];
int ch[maxn][2],fa[maxn],key[maxn],val[maxn],siz[maxn],mn[maxn];
int rt[maxn],sz,seed=233;
set<int> s[maxn];
vector<int> A;
inline int Rand() { return seed=(int)(451312391341ll*seed%2147483647); }
inline int newnode(int v)
{
key[++sz]=Rand();
val[sz]=mn[sz]=v,siz[sz]=1;
ch[sz][0]=ch[sz][1]=fa[sz]=0;
return sz;
}
inline void pushup(int x)
{
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+1;
mn[x]=min(min(mn[ch[x][0]],mn[ch[x][1]]),val[x]);
if(ch[x][0]) fa[ch[x][0]]=x;
if(ch[x][1]) fa[ch[x][1]]=x;
fa[x]=0;
}
void split(int x,int &a,int &b,int k)
{
if(!x) { a=b=0; return; }
if(siz[ch[x][0]]<k) a=x,split(ch[x][1],ch[a][1],b,k-siz[ch[x][0]]-1);
else b=x,split(ch[x][0],a,ch[b][0],k);
pushup(x);
}
void merge(int &x,int a,int b)
{
if(!a || !b) { x=a+b; return; }
if(key[a]<key[b]) x=a,merge(ch[x][1],ch[a][1],b);
else x=b,merge(ch[x][0],a,ch[b][0]);
pushup(x);
}
inline int getid(int x)
{
while(fa[x]) x=fa[x];
return mn[x];
}
inline int getrnk(int x)
{
// puts("getrnk");
int k=siz[ch[x][0]]+1;
while(fa[x])
{
if(x==ch[fa[x]][1])
k+=siz[ch[fa[x]][0]]+1;
x=fa[x];
}
// puts("fin.");
return k;
}
inline int getkth(int x,int k)
{
// printf("getkth %d %d\n",x,k);
while(k>0)
{
if(k==siz[ch[x][0]]+1) return val[x];
if(k<=siz[ch[x][0]]) x=ch[x][0];
else k-=siz[ch[x][0]]+1,x=ch[x][1];
}
return -1;
}
inline void erase(int x)
{
vector<int>::iterator it;
for(it=A.begin();it!=A.end();it++)
if(*it==x) { A.erase(it); break; }
}
inline void del(int x)
{
if(siz[x]<=B) s[siz[x]].erase(mn[x]);
else erase(x);
}
inline void ins(int x)
{
if(siz[x]<=B) s[siz[x]].insert(mn[x]);
else A.push_back(x);
}
void print(int x)
{
if(ch[x][0]) print(ch[x][0]);
printf("%d ",val[x]);
if(ch[x][1]) print(ch[x][1]);
}
inline void check(int &rt)
{
int t=mn[rt],k=getrnk(t),a,b;
split(rt,a,b,k-1);
merge(rt,b,a);
}
inline void S()
{
for(int i=1;i<=B;i++)
if(!s[i].empty())
{
set<int>::iterator it;
printf("len=%d: ",i);
for(it=s[i].begin();it!=s[i].end();it++)
printf("%d ",*it);
putchar('\n');
}
}
int main()
{
// freopen("data.in","r",stdin);
// freopen("czx.out","w",stdout);
mn[0]=0x3f3f3f3f;
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m); B=32;
for(int i=1;i<=n;i++) p[i]=a[i]=i,rt[i]=newnode(i),s[1].insert(i);
char op[10]; ll x,y; int u,v,p,q,c,d,pos,_rt;
for(int Case=1;Case<=m;Case++)
{
// printf("Case %d\n",Case);
scanf("%s%lld%lld",op,&x,&y);
// if(x>y) swap(x,y);
if(op[0]=='s' && op[5]=='a') swap(a[x],a[y]);
else if(op[0]=='s' && op[5]=='p')
{
if(x==y) continue;
// puts("OK!");
u=getid(x),v=getid(y);
// puts("OK!");
if(u!=v)
{
del(rt[u]),del(rt[v]);
p=getrnk(x),q=getrnk(y);
// if(Case==8)
// {
// printf("x=%d y=%d\n",x,y);
// printf("u=%d v=%d\n",u,v);
// printf("p=%d q=%d\n",p,q);
// }
split(rt[u],rt[u],c,p);
split(rt[v],rt[v],d,q);
merge(rt[u],rt[u],d);
merge(rt[u],rt[u],rt[v]);
merge(rt[u],rt[u],c);
rt[v]=0;
_rt=rt[u],rt[u]=0,rt[mn[_rt]]=_rt;
ins(_rt);
}
else
{
del(rt[u]);
p=getrnk(x),q=getrnk(y);
if(p>q) swap(p,q),swap(x,y);
// printf("%d %d %d %d\n",p,q,x,y);
split(rt[u],rt[u],c,q);
split(rt[u],rt[u],d,p);
merge(rt[u],rt[u],c);
// printf("siz %d %d\n",siz[rt[u]],siz[d]);
_rt=rt[u],rt[u]=0,rt[mn[_rt]]=_rt,ins(_rt);
rt[mn[d]]=d,ins(d);
// printf("siz %d %d\n",siz[_rt],siz[d]);
}
}
else
{
if(x==y) { puts("="); continue; }
x--,y--;
pos=n+1;
// puts("OK!");
for(int i=1;i<=B;i++)
if(!s[i].empty() && (y-x)%i!=0)
pos=min(pos,*s[i].begin());
// puts("OK!");
for(auto i:A)
if((y-x)%siz[i]!=0)
pos=min(pos,mn[i]);
if(pos==n+1) { puts("="); continue; }
// printf("%d\n",pos);
u=getid(pos),v=getrnk(pos);
// printf("%d %d\n",u,v);
// printf("%d %d\n",x,y);
// printf("kth %d %d\n",(v-1+x)%siz[rt[u]]+1,(v-1+y)%siz[rt[u]]+1);
p=getkth(rt[u],(v-1+x)%siz[rt[u]]+1);
q=getkth(rt[u],(v-1+y)%siz[rt[u]]+1);
// printf("%d %d\n",p,q);
puts((a[p]<a[q])?"<":">");
}
// for(int i=1;i<=n;i++)
// if(fa[i]==0)
// {
// printf("tree %d: %d\n",i,siz[i]);
// print(i); putchar('\n');
// }
// S();
// for(int i=1;i<=n;i++) printf("%d ",rt[i]); putchar('\n');
// printf("V: ");
// for(int i=0;i<A.size();i++) printf("%d ",A[i]); putchar('\n');
}
for(int i=1;i<=B;i++) s[i].clear();
A.clear(),sz=0,seed=233;
}
return 0;
}
Equivalence in Connectivity
赛后 10min AC,寄
先想 \(\log^3n\) 怎么做。可以通过树剖,将树的问题转化成序列问题,再线段树分治+哈希即可。哈希可以直接异或实现。
同时,对于同一条边来说,我们发现一次 add 和 remove 就是对子树 dfs 序的一段区间进行 \(+1\) 或者 \(-1\),然后将区间的值 \(=1\) 的加入线段树中。那么这样连续的区间最多会有 \(O(|S|)\) 个,考虑在 map 上差分求出这些区间,可以降下一个 log。
时间 \(O(n\log^2 n)\),可以通过此题。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
#define fi first
#define se second
const int maxn=400005;
ull seed=23333333333,base=19260817,t=998244353;
inline ull Rand() { return seed=seed*base+t; }
struct Union_Set
{
int fa[maxn],cnt[maxn],tot;
pii e[10000005]; ull p[maxn],ans;
inline int find(int x)
{
while(x!=fa[x]) x=fa[x];
return x;
}
inline int merge(int x,int y)
{
// printf("merge %d %d\n",x,y);
int u=find(x),v=find(y);
if(u==v) return 0;
if(cnt[u]>cnt[v]) swap(u,v);
fa[u]=v,cnt[v]+=cnt[u];
ans^=p[u]^p[v],p[v]+=p[u],ans^=p[v];
e[++tot]=pii(u,v);
return 1;
}
inline void cancel()
{
int u=e[tot].fi,v=e[tot].se; tot--;
// printf("cancel %d %d\n",u,v);
fa[u]=u,cnt[v]-=cnt[u];
ans^=p[v],p[v]-=p[u],ans^=p[u]^p[v];
}
inline void reset(int cancel_cnt)
{
while(cancel_cnt--) cancel();
}
}s;
int k,n,m,x[maxn],y[maxn],p[maxn],x_[maxn],y_[maxn],st[maxn],ed[maxn],MP[maxn],tim;
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot; char op[maxn][10];
inline void add_edge(int x,int y) { to[++tot]=y,nxt[tot]=head[x],head[x]=tot; }
void dfs(int x,int ff)
{
st[x]=++tim,MP[tim]=x;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==ff) continue;
dfs(y,x);
}
ed[x]=tim;
}
vector<pii> E[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
void update(int rt,int l,int r,int x,int y,int u,int v)
{
// printf("update %d %d %d %d\n",x,y,u,v);
if(x>y) return;
if(x<=l && r<=y) { E[rt].push_back(pii(u,v)); return; }
int mid=(l+r)>>1;
if(x<=mid) update(ls,l,mid,x,y,u,v);
if(y>mid) update(rs,mid+1,r,x,y,u,v);
}
struct node { int x,y,l,r,op; } q[maxn<<1]; int cnt;
inline bool operator < (const node &a,const node &b)
{
if(a.x!=b.x) return a.x<b.x;
if(a.y!=b.y) return a.y<b.y;
if(a.l!=b.l) return a.l<b.l;
if(a.r!=b.r) return a.r<b.r;
return a.op<b.op;
}
map<ull,int> mp;
vector<int> v[maxn];
int ha_cnt;
void calc(int rt,int l,int r)
{
// printf("calc %d %d %d\n",rt,l,r);
int cancel_cnt=0;
for(auto it:E[rt])
cancel_cnt+=s.merge(it.fi,it.se);
if(l==r)
{
if(!mp[s.ans]) mp[s.ans]=++ha_cnt;
v[mp[s.ans]].push_back(MP[l]);
s.reset(cancel_cnt);
return;
}
int mid=(l+r)>>1;
calc(ls,l,mid);
calc(rs,mid+1,r);
s.reset(cancel_cnt);
}
map<int,int> tr;
int main()
{
// freopen("data.in","r",stdin);
// freopen("czx.out","w",stdout);
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d",&k,&n,&m); s.ans=0;
for(int i=1;i<=n;i++) s.p[i]=Rand(),s.ans^=s.p[i],s.fa[i]=i,s.cnt[i]=1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&x[i],&y[i]);
if(x[i]>y[i]) swap(x[i],y[i]);
q[++cnt]=(node){x[i],y[i],1,k,0};
}
for(int i=2;i<=k;i++)
{
scanf("%d%s%d%d",&p[i],op[i],&x_[i],&y_[i]),add_edge(p[i],i);
if(x_[i]>y_[i]) swap(x_[i],y_[i]);
}
dfs(1,0);
// for(int i=2;i<=k;i++) printf("%d %d\n",p[i],i);
// for(int i=1;i<=k;i++) printf("%d %d\n",st[i],ed[i]);
for(int i=2;i<=k;i++)
q[++cnt]=(node){x_[i],y_[i],st[i],ed[i],(op[i][0]=='a')?0:1};
sort(q+1,q+cnt+1);
for(int l=1,r=0;l<=cnt;l=r+1)
{
r=l;
for(;r<cnt && q[r].x==q[r+1].x && q[r].y==q[r+1].y;r++);
// printf("(%d,%d)\n",q[l].x,q[l].y);
// for(int i=l;i<=r;i++)
// printf("%d %d %d\n",q[i].l,q[i].r,q[i].op);
tr.clear();
for(int i=l;i<=r;i++)
if(q[i].op==0) tr[q[i].l]++,tr[q[i].r+1]--;
else tr[q[i].l]--,tr[q[i].r+1]++;
int cur=0,las=-1;
for(auto o:tr)
{
if(las!=-1 && cur>0)
update(1,1,k,las,o.fi-1,q[l].x,q[l].y);
cur+=o.se,las=o.fi;
}
}
// puts("OK!");
calc(1,1,k);
printf("%d\n",ha_cnt);
for(int i=1;i<=ha_cnt;i++)
{
printf("%d ",(int)v[i].size());
sort(v[i].begin(),v[i].end());
for(int j=0;j<(int)v[i].size();j++)
printf("%d ",v[i][j]);
putchar('\n');
}
for(int i=1;i<=k;i++) head[i]=0;
for(int i=1;i<=ha_cnt;i++) v[i].clear();
for(int i=1;i<=(k<<2);i++) E[i].clear();
tim=tot=cnt=ha_cnt=s.tot=0,mp.clear();
}
return 0;
}