Loading

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,摆了。

posted @ 2026-03-03 21:59  HD0X  阅读(0)  评论(0)    收藏  举报