20250602比赛总结

T1 皮胚 (match)

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

简单 dp,设 \(dp_{i,j}\) 表示原串第 \(i\) 位,匹配串第 \(j\) 位有没有解

如果当前是 . 或字母,就是一对一,如果是 *,那么看等价的位置是否有转移

此时可以刷表,因为只会填 \(n^2\) 个位置,时间复杂度 \(O(n^2)\)

但是注意最后可能是 *,要注意跑没跑到,不然随机挂

T2 核冰 (merge)

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

线段树就是一个神经的东西

40分:按题意模拟即可

贪心显然,就是能合则合,但是原有的至少留 1 个

显然合并前和合并后的奇偶性是一致的,将合并前的数量组成的数组记为 cnt

观察发现,如果将每一次修改看作一次加入和一次删除

加入就是找到第一个合并后的 1 或 0 去增加,中间的 2 全部 -1,删除就是找到第一个 2,或者没有向前合并的 1 减少,中间的 1 全部 +1

把这个放到 cnt 数组上,加入就是找奇数或 0,删除是找偶数或 1

但这样正常的线段树显然不行,可以使用不严格的线段树二分

这里记录两个标记 t1,t0,即是否全为 1/0,然后记录段最小值,看能否段内更新

这样如果左边全不满足,就要再递归右边,所以时间不是严格的 log

点击查看代码
#include<cstdio>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
using namespace std;
const int N=5e5;
int n,m,a[1000005],cnt[1000005],ans;
struct seg_tr{
	int l,r,t1,t0,mn,lazy;
}tr[4000005];
void pushup(int id)
{
	tr[id].t0=tr[lid].t0&tr[rid].t0;
	tr[id].t1=tr[lid].t1&tr[rid].t1;
	tr[id].mn=min(tr[lid].mn,tr[rid].mn);
}
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r;
	if(l==r)
	{
		tr[id].mn=cnt[l];
		if(cnt[l]%2==0) tr[id].t0=1;
		else tr[id].t1=1;
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
	pushup(id);
}
void update(int id,int x)
{
	tr[id].mn+=x;
	tr[id].lazy+=x;
	if(x%2) swap(tr[id].t0,tr[id].t1);
}
void pushdown(int id)
{
	if(tr[id].lazy)
	{
		update(lid,tr[id].lazy);
		update(rid,tr[id].lazy);
		tr[id].lazy=0;
	}
}
int add(int id,int x)
{
	if(tr[id].l>=x)
	{
		if(tr[id].t0&&tr[id].mn)
		{
			update(id,1);
			return tr[id].r;
		}
		if(tr[id].l==tr[id].r)
		{
			if(!tr[id].mn)
			{
				tr[id].mn++,tr[id].t0=0,tr[id].t1=1;
				ans++;
			}
			else update(id,1);
			return tr[id].r-1;
		}
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1,tmp=0;
	if(x<=mid) tmp=add(lid,x);
	if(x>mid||tmp==mid) tmp=add(rid,x);
	pushup(id);
	return tmp;
}
int del(int id,int x)
{
	if(tr[id].l>=x)
	{
		if(tr[id].t1&&tr[id].mn>1)
		{
			update(id,-1);
			return tr[id].r;
		}
		if(tr[id].l==tr[id].r)
		{
			if(tr[id].mn==1)
			{
				tr[id].mn=0,tr[id].t0=1,tr[id].t1=0;
				ans--;
			}
			else update(id,-1);
			return tr[id].r-1;
		}
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1,tmp=0;
	if(x<=mid) tmp=del(lid,x);
	if(x>mid||tmp==mid) tmp=del(rid,x);
	pushup(id);
	return tmp;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		cnt[a[i]]++;
	}
	for(int i=1;i<=N;i++)
	{
		cnt[i]=(cnt[i-1]-1)/2+cnt[i];
		if(cnt[i]>0) ans++;
	}
	build(1,1,N);
	while(m--)
	{
		int opt,x,y;
		scanf("%d",&opt);
		if(opt==1)
		{
			scanf("%d%d",&x,&y);
			int tmp=del(1,a[x]);
			a[x]=y;
			tmp=add(1,a[x]);
		}
		else printf("%d\n",ans);
	}
	return 0;
}

T3 方珍 (mex)

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

先考虑如何找第 k 大的,如果要统计 mex 在某范围内的值,显然只需要前面的值都存在即可

所以对于单行,可以二分+双指针

但是多行的情况下,考虑什么时候 ans 会变化,可以先按 w 从大到小排序,那么当 \(mex>ans-w_i\) 时才更新

所以判断过去即可,因为这个东西至多增加 n 次,时间复杂度 \(O(n^2)\)

点击查看代码
#include<bits/stdc++.h>
#define ull unsigned long long
using namespace std;
int n,k[10004],w[10005],m[10004],a[10004],id[10004];
int cnt[10005];
ull seed[10004];
ull _rand(int x)
{
	seed[x]^=seed[x]<<13;
	seed[x]^=seed[x]>>7;
	seed[x]^=seed[x]<<17;
	return seed[x];
}
bool cmp(int x,int y)
{
	return w[x]>w[y];
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
   		scanf("%d%d%d%llu",&k[i],&w[i],&m[i],&seed[i]);
   		id[i]=i;
	}
	int tmp=(n+1)*n/2;
	sort(id+1,id+n+1,cmp);
	int v=1,ans=0;
	for(int x=1;x<=n;x++)
	{
//		printf("%d %d\n",x,id[x]);
		for(int i=1;i<=n;i++)
		{
			a[i]=_rand(id[x])%m[id[x]];
//			printf("%d ",a[i]);
		}
//		printf("a\n");
		v=max(v,ans-w[id[x]]);
		for(;v<=m[id[x]]+1;v++)
		{
			int i=1,j=1,p=0,sum=0;
			for(;i<=n+1;i++)
			{
				if(p==v) break;
				if(a[i]<v) cnt[a[i]]++;
				while(cnt[p]) p++;
			}
			if(p!=v) break;
			while(j<i&&(a[j]>=v||cnt[a[j]]>1))
			{
				if(a[j]<v) cnt[a[j]]--;
				j++;
			}
			sum=j;
//			printf("%d %d\n",i,j);
			for(;i<=n;i++)
			{
				if(a[i]<v) cnt[a[i]]++;
				while(j<=i&&(a[j]>=v||cnt[a[j]]>1))
				{
					if(a[j]<v) cnt[a[j]]--;
					j++;
				}
				sum+=j;
			}
			
			for(i=0;i<=n;i++) cnt[i]=0;
//			printf("%d %d %d %d\n",id[x],x,v,sum);
			if(sum<tmp-k[id[x]]+1) break;
		}
		ans=max(ans,v-1+w[id[x]]);
		v--;
	}
	printf("%d",ans);
	return 0;
}

T4 术劣 (sequence)

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

有一个性质 满足 \(gcd(a_2-a_1,\dots,a_n-a_{n-1})*(n-1)=max\{a_i\}-min\{a_i\}\) 的序列排序后是等差数列

变形并替换一下得到 \(d*r=l*d+max\{a_i\}-min\{a_i\}\)

对于 max 和 min 直接单调栈在线段树上更新即可,关键是 d

因为这个东西满足交换律,所以可以把到 r-1 gcd 相同的直接合起来,如果发生变化,单点修改

因为 gcd 至多变 logn 次,时间复杂度 \(O(nlog^2n)\)

因为右边的值最小就是 \(d*r\),所以线段树统计最小值和最小值个数即可

点击查看代码
#include<cstdio>
#include<cmath>
#include<algorithm>
#define lid id<<1
#define rid id<<1|1
#define ll long long
using namespace std;
int gcd(int a,int b)
{
	if(b==0) return a;
	return gcd(b,a%b);
}
int n,a[200005],f[200005],l[200005],st1[200005],st2[2000005],t1,t2;
ll b[200005];
int find(int x)
{
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
struct seg_tr{
	int l,r,cnt;
	ll mn,lazy;
}tr[800005];
void build(int id,int l,int r)
{
	tr[id].l=l,tr[id].r=r,tr[id].cnt=r-l+1;
	if(l==r)
	{
		return;
	}
	int mid=(l+r)>>1;
	build(lid,l,mid);
	build(rid,mid+1,r);
}
void pushdown(int id)
{
	if(tr[id].lazy)
	{
		tr[lid].lazy+=tr[id].lazy;
		tr[lid].mn+=tr[id].lazy;
		tr[rid].lazy+=tr[id].lazy;
		tr[rid].mn+=tr[id].lazy;
		tr[id].lazy=0;
	}
}
void pushup(int id)
{
	tr[id].mn=min(tr[lid].mn,tr[rid].mn);
	tr[id].cnt=0;
	if(tr[lid].mn==tr[id].mn) tr[id].cnt=tr[lid].cnt;
	if(tr[rid].mn==tr[id].mn) tr[id].cnt+=tr[rid].cnt;
}
void update(int id,int l,int r,ll x)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
		tr[id].mn+=x,tr[id].lazy+=x;
		return;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) update(lid,l,r,x);
	else if(l>mid) update(rid,l,r,x);
	else update(lid,l,mid,x),update(rid,mid+1,r,x);
	pushup(id);
}
ll query(int id,int l,int r,ll k)
{
	if(tr[id].l==l&&tr[id].r==r)
	{
		if(tr[id].mn==k) return tr[id].cnt;
		return 0;
	}
	pushdown(id);
	int mid=(tr[id].l+tr[id].r)>>1;
	if(r<=mid) return query(lid,l,r,k);
	else if(l>mid) return query(rid,l,r,k);
	else return query(lid,l,mid,k)+query(rid,mid+1,r,k);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		f[i]=l[i]=i;
	}
	build(1,1,n);
	ll ans=1;
	for(int r=1;r<=n;r++)
	{
		while(t1&&a[st1[t1]]>=a[r])
		{
			update(1,st1[t1-1]+1,st1[t1],a[st1[t1]]-a[r]);
			t1--;
		}
		while(t2&&a[st2[t2]]<=a[r])
		{
			update(1,st2[t2-1]+1,st2[t2],a[r]-a[st2[t2]]);
			t2--;
		}
		st1[++t1]=r,st2[++t2]=r;
		if(r==1)
		{
			printf("1 ");
			continue;
		}
		int now=abs(a[r]-a[r-1]),lst=r-1;
		b[r-1]=now;
		update(1,r-1,r-1,1ll*now*(r-1));
		for(int i=r-2;i>0;i--)
		{
			i=find(i);
			now=gcd(now,b[i]);
			if(now!=b[i])
			{
				for(int j=l[i];j<lst;j++) update(1,j,j,1ll*j*now-1ll*j*b[i]);
				b[i]=now;
			}
			if(b[i]==b[find(lst)])
			{
				int x=find(lst),y=find(i);
				f[x]=y;
				l[y]=min(l[x],l[y]);
			}
			i=lst=l[find(i)];
		}
		ans++;
		lst=r;
		for(int i=r-1;i>0;i--)
		{
			i=find(i);
			ans+=query(1,l[i],lst-1,1ll*b[i]*r);
			i=lst=l[find(i)];
		}
		printf("%lld ",ans);
	}
	return 0;
}
posted @ 2025-06-02 11:46  wangsiqi2010916  阅读(19)  评论(0)    收藏  举报