20250322比赛总结

反正是很唐,主要出在了答题策略上,误判了 T2 和 T4 的难度,而且 T2 没有二次拆贡献,导致方法极为复杂

类似情况其实可以果断放弃,其实问题跟 CSP 一模一样

T1 交错代码也是离谱

T1 光

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

枚举对角线格子,然后可以记右上角的值为 \(x\), 左下角为 \(y\),除去枚举点这两个格子还需要 \(t1,t2\) 的贡献,另外两个格子还需要 \(p\) 的贡献,那么

\[\begin{cases}\lfloor\frac x2\rfloor+\lfloor\frac y2\rfloor\ge p\\ x+\lfloor\frac y4\rfloor\ge t1\\ y+\lfloor\frac x4\rfloor\ge t2\end{cases} \]

可以发现下面两个式子的 \(x+y\) 是单峰的,可以二分,对于剩下的部分,因为 \(x,y\) 等价,所以前面的可以在找到最小的 \(x+y\) 后再去满足

主要是害怕自己的做法假,加了暴力,条件又没改

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int a,b,c,d,w,x,y,z;
int get(int mid)
{
	int tmp1=x+mid,tmp2=y+mid/4;
	return max(0,max(4*(b-tmp1),c-tmp2))+mid;
}
int main()
{
	scanf("%d%d%d%d",&a,&b,&c,&d);
	int ans=1e9;
	for(int i=0;i<=a;i++)
	{
		for(int j=0;j<=d;j++)
		{
			w=i+j/4,x=i/2+j/2,y=i/2+j/2,z=j+i/4;
			int l=0,r=b;
			while(l<r)
			{
//				if(i==22&&j==0)printf("%d %d\n",l,r);
				int mid=(l+r)>>1;
				if(get(mid)<=get(mid+1)) r=mid;
				else l=mid+1;
			}
			int t=get(l)-l,tmp=max(a-w,max(0,d-z));
//			if(i==22&&j==0) printf("%d %d ",l,t);
			if(l/2+t/2<tmp)
			{
				if(l%2) l++;
				if(l/2+t/2<tmp&&t%2) t++;
				if(l/2+t/2<tmp) l+=tmp*2;
			}
			ans=min(ans,l+t+i+j);
//			if(l+t+i+j+tmp==638) printf("%d %d %d %d %d ",l,t,i,j,tmp);
		}
	}
	printf("%d",ans);
	return 0;
}

T2 爬

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

显然拆位求贡献,其实每个节点的情况是互不干涉的,先看非根结点怎么求

如果当前位可移动到当前节点的权值中有 1,那么如果记有 k 个节点可能会到这里,方案数就是 \(2^{k-1}\),因为其他节点互不干涉,所以总方案数就是 \(2^{n-2}\)

对于根,因为他的权值不能动,所以如果这个位置是 1,而且儿子都是 0,那么方案数就是 \(2^{n-1}\)

然后减去只留下一个点的贡献,两种情况,一个是儿子和本身不动,二是父亲不为 1,移动到父亲,父亲的儿子不动

点击查看代码
#include<cstdio>
#define ll long long
using namespace std;
const int mod=1e9+7;
int n,edgenum,head[100005],d[100005];
int b[100005][35],p[35],f[100005];
ll qpow(ll x,int y)
{
	ll res=1;
	while(y)
	{
		if(y&1) res=res*x%mod;
		x=x*x%mod;
		y>>=1;
	}
	return res;
}
struct edge{
	int to,nxt;
}e[200005];
void add_edge(int u,int v)
{
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
void get(int id,int x)
{
	for(int i=1;i<=32;i++)
	{
		b[id][i]=x&1;
		x>>=1;
	}
}
ll sum=0,a[100005];
void solve(int u)
{
	for(int i=1;i<=32;i++) p[i]=b[u][i];
	for(int i=head[u];i;i=e[i].nxt)
	{
		int v=e[i].to;
		for(int j=1;j<=32;j++)
		{
			p[j]|=b[v][j];
		}
	}
	for(int i=1;i<=32;i++)
	{
		if(p[i])
		{
			sum=(sum+qpow(2,n-2)*(1ll<<(i-1))%mod)%mod;
		}
	}
	sum=(sum-qpow(2,n-2-d[u])*a[u]%mod+mod)%mod;
	if(f[u]!=1)sum=(sum-qpow(2,n-2-d[f[u]])*a[u]%mod+mod)%mod;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&a[i]);
		get(i,a[i]);
	}
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&f[i]);
		add_edge(f[i],i);
		d[f[i]]++;
	}
	for(int i=2;i<=n;i++)
	{
		solve(i);
//		printf("%d ",sum);
	}
	for(int i=1;i<=32;i++) p[i]=0;
	for(int i=head[1];i;i=e[i].nxt)
	{
		int v=e[i].to;
		for(int j=1;j<=32;j++)
		{
			p[j]|=b[v][j];
		}
	}
	for(int i=1;i<=32;i++)
	{
		if(p[i]) sum=(sum+qpow(2,n-2)*(1ll<<(i-1))%mod)%mod;
		if(b[1][i]&&!p[i]) sum=(sum+qpow(2,n-1)*(1ll<<(i-1))%mod)%mod;
	}
	sum=(sum-qpow(2,n-1-d[1])*a[1]%mod+mod)%mod;
	printf("%lld",sum);
	return 0;
}

T3 字符串

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

可以先强制 B 开头,A 结尾,然后枚举段数,先放成 c 个 B + 1 个 A 的形式,此时,如果 A 有剩余,在开头放一个,B 有剩余,在结尾放一个

如果 A 还有剩余,那么每 \(a\) 个会有一个贡献

如果 B 还有剩余,现将所有段填成 \(kb+1\) 的形式,剩下的就是每 \(b\) 个一点贡献

点击查看代码
#include<cstdio>
#include<algorithm>
using namespace std;
int T,n,m,a,b,c;
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d%d%d%d",&n,&m,&a,&b,&c);
		int ans=1+(n-1)/a+(m-1)/b;
		for(int i=1;i<=min(m/c,n);i++)
		{
			int sum=i*2-1;
			if(n-i>0)
			{
				sum=sum+1+(n-i-1)/a;
			}
			if(c>b)
			{
				sum+=(c-1)/b*i;
			}
			if(m-c*i>0)
			{
				sum++;
				int tmp=m-c*i-1;
				int x=(b*((c+b-1)/b)+1)-c;
//				printf("%d ",x);
				if(1ll*x*i>=tmp) sum+=tmp/x;
				else
				{
					sum+=i+(tmp-i*x)/b;
				}
			}
			ans=max(ans,sum);
		}
		printf("%d\n",ans+1);
	}
	return 0;
}

T4 奇怪的函数

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

性质1提示很明显,是分段函数,而且永远是三段

性质2的提示是函数可以拆分成任意段,然后得到每一段的操作方式,然后合并

所以可以分块处理,求出每个快的分段情况,然后带值即可

最后修改时暴力重构快即可

点击查看代码
#include<cstdio>
#include<algorithm>
#include<cmath>
#define int long long
using namespace std;
int n,a[100005],b[100005],blen,pos[100005],t;
int st[100005],ed[100005],q;
struct node{
	int l,r,vl,vr,t;
}c[100005];
void get(int x)
{
	int l=1,r=1e18,vl=1,vr=1e18,sum=0;
	for(int i=st[x];i<=ed[x];i++)
	{
		if(a[i]==1) vl+=b[i],vr+=b[i],sum+=b[i];
		if(a[i]==2)
		{
			if(vl>=b[i])
			{
				l=r+1;
				break;
			}
			int y=b[i]-vl;
			r=min(r,l+y),vr=min(vr,b[i]);
		}
		if(a[i]==3)
		{
			if(vr<=b[i])
			{
				l=r+1;
				break;
			}
			int y=vr-b[i];
			l=max(l,r-y),vl=max(vl,b[i]);
		}
	}
	if(l>r)
	{
		int p=1;
		for(int i=st[x];i<=ed[x];i++)
		{
			if(a[i]==1) p+=b[i];
			if(a[i]==2) p=min(p,b[i]);
			if(a[i]==3) p=max(p,b[i]);
		}
		c[x]=(node){-1,0,p,0,0};
		return;
	}
	vl=l-1;
	for(int i=st[x];i<=ed[x];i++)
	{
		if(a[i]==1) vl+=b[i];
		if(a[i]==2) vl=min(vl,b[i]);
		if(a[i]==3) vl=max(vl,b[i]);
	}
	vr=r+1;
	for(int i=st[x];i<=ed[x];i++)
	{
		if(a[i]==1) vr+=b[i];
		if(a[i]==2) vr=min(vr,b[i]);
		if(a[i]==3) vr=max(vr,b[i]);
	}
	c[x]=(node){l,r,vl,vr,sum};
//	printf("%lld %lld %lld %lld %lld\n",l,r,vl,vr,sum);
}
signed main()
{
//	freopen("1.txt","r",stdin);
	scanf("%lld",&n);
	blen=sqrt(n);
	t=n/blen;
	if(n%blen) t++;
//	printf("%d ",t);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld%lld",&a[i],&b[i]);
	}
	for(int i=1;i<=t;i++)
	{
		st[i]=(i-1)*blen+1;
		ed[i]=i*blen;
	}
	ed[t]=n;
	for(int i=1;i<=t;i++)
	{
		for(int j=st[i];j<=ed[i];j++)
		{
			pos[j]=i;
		}
		get(i);
	}
	scanf("%lld",&q);
	while(q--)
	{
		int opt,x,val;
		scanf("%lld%lld",&opt,&x);
		if(opt<=3)
		{
			scanf("%lld",&val);
			a[x]=opt,b[x]=val;
			get(pos[x]);
		}
		else
		{
			for(int i=1;i<=t;i++)
			{
				if(c[i].l==-1) x=c[i].vl;
				else
				{
					if(x<c[i].l) x=c[i].vl;
					else if(x>c[i].r) x=c[i].vr;
					else x+=c[i].t;
				}
			}
			printf("%lld\n",x);
		}
	}
	return 0;
}
posted @ 2025-03-22 18:19  wangsiqi2010916  阅读(20)  评论(0)    收藏  举报