20250705比赛总结

T1 异或

https://www.gxyzoj.com/d/hzoj/p/4718

因为只涉及一次查询,所以可以直接差分

难点在于是三角形,但是因为只有两条边,所以可以对纵向做一次,斜向做一次

点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
int n,q;
ll a[2005][2005],b[2005][2005],c[2005],d[2005];
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=q;i++)
	{
		ll x,y,l,s;
		scanf("%lld%lld%lld%lld",&x,&y,&l,&s);
		a[x+l-1][y+l]-=s;
		a[x-1][y]+=s;
		b[x+l-1][y]+=s,b[x-1][y]-=s;
	}
	ll ans=0;
	for(int i=n*2;i>=1;i--)
	{
		for(int j=1;j<=n*2;j++)
		{
			c[j]=c[j+1]+a[i][j];
			d[j]+=b[i][j];
		}
		c[2*n+1]=a[i][2*n+1];
		ll x=0;
		for(int j=1;j<=n;j++)
		{
			x+=d[j]+c[j];
			if(i<=n)
			ans^=x;
		}
//		printf("\n");
	}
	printf("%lld",ans);
	return 0;
}

T2 游戏

https://www.gxyzoj.com/d/hzoj/p/4719

显然,每一次操作都会将集合一分为二,每个数只会归属于 1 个集合

所以考虑直接搜索,对于空集直接 return ,这样可以解决 \(m\le 100\)

接下来可以发现,当 m 很大时都是 0,可以进行猜想,当 m 大于一个值时,必然答案为 0

对于先手,要让和最小,每次至少让集合减半,后手同理,所以只需要 \(2logn\) 次就会没有数

点击查看代码
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
int n,m;
ll a[20004],b[100005],c[20004];
ll dfs(int x,int len)
{
	if(len==0) return 0;
	ll fl=0,sum=0;
	for(int i=1;i<=len;i++)
	{
		sum+=c[i];
	}
	if(x==m+1) return sum;
	for(int i=1;i<=len;i++)
	{
		if(c[i]%b[x]) fl++;
	}
	if(fl==0||fl==len)
	{
		if(x%2) return min(0ll,dfs(x+1,len));
		else return max(0ll,dfs(x+1,len));
	}
	ll cnt=0,s,t;
	ll tmp[20001];
	for(int i=1;i<=len;i++) tmp[i]=c[i];
	for(int i=1;i<=len;i++)
	{
		if(tmp[i]%b[x])
		c[++cnt]=tmp[i];
	}
	s=dfs(x+1,cnt);
	cnt=0;
	for(int i=1;i<=len;i++)
	{
		if(tmp[i]%b[x]==0)
		c[++cnt]=tmp[i];
	}
	t=dfs(x+1,cnt);
	if(x%2) return min(s,t);
	else return max(s,t);
}
int main()
{
//	freopen("1.txt","r",stdin);
	scanf("%d%d",&n,&m);
	if(m>100)
	{
		printf("0");
		return 0;
	}
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		c[i]=a[i];
	}
	int fl=0;
	for(int i=1;i<=m;i++)
	{
		scanf("%lld",&b[i]);
		if(i!=1&&b[i]!=b[i-1]) fl=1;
	}
	if(!fl&&m>10)
	{
		printf("0");
		return 0;
	}
	printf("%lld",dfs(1,n));
	return 0;
}

T3 连通块

https://www.gxyzoj.com/d/hzoj/p/4720

暴力就是两两建边然后枚举每个可能删除的点

优化也就分为两部分,建边和删点

首先是建边,如果能连边,那么公约数必然含有两个质数相乘得到的合数,所以考虑建一些虚点

因为每个数所含的质数是有限的,所以至多有 \(nlog^2V\) 级别的点

接下来是删点,显然要在最大的连通块内找割点,但是不能对每个割点进行删除

所以考虑在跑 tarjan 的时候统计,分为两种情况,一是构成点双,那么点都在栈中,没有加,依次弹栈统计即可

另一种是不处于边双,直接加

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e7,M=2.1e6;
int T,n,p[1000006],cnt,minp[N+1],cnt1,id[N+1],a[100005];
int b[1005],edgenum,head[M],f[M],g[M];
bool vis[N+1],c[1005];
struct edge{
	int to,nxt;
}e[N+5];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
void prime()
{
	for(int i=2;i<=N;i++)
	{
		if(!vis[i]) p[++cnt]=i,minp[i]=i;
		else if(!vis[i/minp[i]]) id[i]=++cnt1;
		for(int j=1;p[j]*i<=N;j++)
		{
			vis[i*p[j]]=1,minp[i*p[j]]=p[j];
			if(i%p[j]==0) break;
		}
	}
}
int find(int x)
{
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
void link(int x,int u)
{
	int tmp=0;
	while(x>1)
	{
		int p1=minp[x];
		b[++tmp]=minp[x];
		x/=p1;
		while(x%p1==0)
		{
			c[tmp]=1;
			x/=p1;
		}
	}
	for(int i=1;i<=tmp;i++)
	{
		if(c[i])
		{
			int v=id[b[i]*b[i]]+n;
			add_edge(u,v),add_edge(v,u);
			int s=find(u),t=find(v);
			if(s!=t)
			{
				f[s]=t,g[t]+=g[s];
			}
			c[i]=0;
		}
		for(int j=1;j<i;j++)
		{
			int v=id[b[i]*b[j]]+n;
			add_edge(u,v),add_edge(v,u);
			int s=find(u),t=find(v);
			if(s!=t)
			{
				f[t]=s,g[s]+=g[t];
			}
		}
	}
}
int dfn[M],low[M],idx,st[M],siz[M],top,fi,se,mxid,ans;
void tarjan(int u,int in_edge)
{
	dfn[u]=low[u]=++idx,st[++top]=u,siz[u]=0;
	int res=0,fl=0;
	for(int i=head[u];i;i=e[i].nxt)
	{
		if(i==(in_edge^1)) continue;
		int v=e[i].to;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]) fl++;
			if(low[v]>dfn[u])
			{
				top--,siz[u]+=siz[v];
				res=max(res,siz[v]);
			}
			else if(low[v]==dfn[u])
			{
				int t=0,s=0;
				while(t!=v)
				{
					t=st[top--];
					siz[u]+=siz[t],s+=siz[t];
				}
				res=max(res,s);
			}
		}
		else low[u]=min(low[u],dfn[v]);
	}
	if(u<=n)
	{
		siz[u]++;
		if((fl&&in_edge)||(fl>1&&!in_edge)) ans=min(ans,max(fi-siz[u],res));
		else ans=min(ans,fi-1);
	}
}
int main()
{
	scanf("%d",&T);
	prime();
	while(T--)
	{
		scanf("%d",&n);
		for(int i=1;i<=n+cnt1;i++) head[i]=g[i]=dfn[i]=0,f[i]=i;
		for(int i=1;i<=n;i++) g[i]=1;
		edgenum=1,idx=top=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			link(a[i],i);
//			printf("a");
		}
		fi=0,se=0;
		for(int i=1;i<=n+cnt1;i++)
		{
			if(f[i]!=i||g[i]==0) continue;
			if(g[i]>fi)
			{
				se=fi;
				fi=g[i],mxid=i;
			}
			else if(g[i]>se) se=g[i];
		}
		ans=fi;
		tarjan(mxid,0);
		printf("%d\n",max(ans,se));
	}
	return 0;
}
posted @ 2025-07-05 17:18  wangsiqi2010916  阅读(34)  评论(0)    收藏  举报