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;
}

浙公网安备 33010602011771号