3月2日
打了场省选,睡了3小时说是。
喜提 \(15 + 0 + 0 = 15pts\) ,荣获 \(rk\) \(inf\)。
T1
ad-hoc 诈骗题。
发现查询可以弱化为查异或和,把前三种操作改为维护集合异或和即可。
全cw仅jmr未被诈骗。。。
点击查看代码
#include <bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int MAXN=1e6+10;
int lstans=0,Q;
int a[MAXN];
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>Q;
for(int i=0,p=1;p<=Q;p++)
{
int op;
cin>>op;
op^=lstans;
if(op==1)
{
int M,K;
cin>>M>>K;
M^=lstans,K^=lstans;
a[++i]=(K&1)*M;
}
if(op==2)
{
int X,Y;
cin>>X>>Y;
X^=lstans,Y^=lstans;
a[++i]=a[X]^a[Y];
}
if(op==3)
{
int X,M,K;
cin>>X>>M>>K;
X^=lstans,M^=lstans,K^=lstans;
a[++i]=a[X]^((K&1)*M);
}
if(op==4)
{
int X;
cin>>X;
X^=lstans;
int ans=a[X];
cout<<ans<<"\n";
lstans=ans;
}
}
}
T2
牛牛题。空间 \(\text{80 MB}\)。
容易发现对颜色建一张有先后顺序的图后,每次肯定选一个强联通分量删去,那么 \(O(n^2)\) 是容易做到的。
考虑不建出图,直接判哪些颜色缩在一起。有两种方法,一种较复杂的依据 tarjan,写树剖和线段树的方式,一种较简单但细节很多的依据 kosaraju,写 dfs 序线段树的做法。我选用了第二种。
考虑 kosaraju 的过程:跑出后序遍历顺序,和对反图按逆后序遍历顺序直接搜,搜得到即在一个强联通分量内。
跑后序遍历可以遍历没到过的颜色,对这种颜色的所有点,从它向它们的 lca 遍历路上遇到的颜色即可。过程可用路径压缩并查集实现,找到新颜色直接递归。复杂度是 \(O(n\log n)\)。
后面的遍历也类似,按逆后序遍历顺序遍历颜色,对它的每个点的子树内,查经过该点会向该颜色连边的颜色。所以线段树维护每个点的颜色共同 lca 的深度,标记到达就把值改为 inf。查询则直接暴力找到每个合法颜色,可以证明是均摊 \(O(n\log n)\)。
就结束了,但是由于本题最大的问题是卡空间,所以要写树剖 lca,还要复用多个数组,所以代码可读性很差。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN=5e5+10;
int n,a[MAXN],k,lst[MAXN],fa[MAXN],top[MAXN],mx[MAXN],ret[MAXN],cnt;
int son[MAXN],d[MAXN],head[MAXN],stk[MAXN],t[MAXN<<2],idx[MAXN],sum;
long long ans;
int read()
{
int x=0;char ch=getchar();
while(ch<'0'||ch>'9') ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
struct edge{
int v,nxt;
}e[MAXN<<1];
void build(int l,int r,int rt)
{
if(l==r)
{
t[rt]=head[a[ret[l]]];
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1),build(m+1,r,rt<<1|1);
t[rt]=min(t[rt<<1],t[rt<<1|1]);
}
void update(int l,int r,int p,int rt)
{
if(l==r)
{
t[rt]=n+1;
return;
}
int m=(l+r)>>1;
if(p<=m) update(l,m,p,rt<<1);
else update(m+1,r,p,rt<<1|1);
t[rt]=min(t[rt<<1],t[rt<<1|1]);
}
void query(int l,int r,int L,int R,int res,int rt);
void deal(int x)
{
top[x]=0;++sum;
int u=lst[x];
do{
update(1,n,idx[u],1);
++cnt;
}while((u=son[u])>0);
u=lst[x];
do{
query(1,n,idx[u]+1,mx[u],d[u],1);
}while((u=son[u])>0);
}
void query(int l,int r,int L,int R,int res,int rt)
{
if(t[rt]>res || L>R) return;
if(l==r)
{
return deal(a[ret[l]]);
}
int m=(l+r)>>1;
if(L<=m) query(l,m,L,R,res,rt<<1);
if(m<R) query(m+1,r,L,R,res,rt<<1|1);
}
void adde(int u,int v)
{
e[++k]={v,head[u]},head[u]=k;
e[++k]={u,head[v]},head[v]=k;
}
void dfs(int u,int fat)
{
fa[u]=fat;
d[u]=d[fat]+1;
top[u]=1;
ret[++k]=u;idx[u]=mx[u]=k;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v!=fat)
{
dfs(v,u),top[u]+=top[v];
mx[u]=mx[v];
if(top[son[u]]<top[v]) son[u]=v;
}
}
}
void cut(int u)
{
if(son[u]) top[son[u]]=top[u],cut(son[u]);
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].v;
if(v!=fa[u] && v!=son[u]) top[v]=v,cut(v);
}
}
int lca(int u,int v)
{
while(top[u]!=top[v])
{
if(d[top[u]]<d[top[v]]) v=fa[top[v]];
else u=fa[top[u]];
}
if(d[u]<d[v]) return u;
return v;
}
int find(int u)
{
return u?(top[a[fa[u]]]?fa[u]=find(fa[u]):fa[u]):0;
}
void solve(int x)
{
top[x]=1;
int u=lst[x];
do{
int p=find(u);
while(d[p]>=head[x])
{
solve(a[p]);
p=find(p);
}
}while((u=son[u])>0);
stk[++k]=x;
}
signed main()
{
n=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1,u,v;i<n;i++) u=read(),v=read(),adde(u,v);
k=0,dfs(1,0),k=0;
for(int i=1;i<=n;i++) top[i]=0;
top[1]=1,cut(1);
for(int i=1;i<=n;i++) head[i]=0;
for(int i=1;i<=n;i++)
{
if(head[a[i]]) head[a[i]]=lca(head[a[i]],i),son[i]=lst[a[i]],lst[a[i]]=i;
else head[a[i]]=lst[a[i]]=i,son[i]=0;
}
for(int i=1;i<=n;i++) head[i]=d[head[i]];
build(1,n,1);
for(int i=1;i<=n;i++) top[i]=stk[i]=0;
for(int i=1;i<=n;i++)
{
if(lst[i] && !top[i])
{
solve(i);
}
}
for(int i=k;i;i--) if(top[stk[i]])
{
sum=0,cnt=0;
deal(stk[i]);
ans+=1ll*sum*cnt;
}
cout<<ans;
}
T3
传统容斥计数 DP,摆了。

浙公网安备 33010602011771号