多校A层冲刺NOIP2024模拟赛06

小 Z 的手套(gloves)

二分枚举即可

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=2e5+107;
int n,m;
int l[N],r[N];

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

bool check(int ans)
{
	if(r[1]+ans>=l[n]&&r[m]-ans<=l[1]) return 1;
	int x=upper_bound(l+1,l+1+n,r[1]-ans)-(l+1);
	if(l[x]>r[1]+ans) return 0;
	for(int i=1;i<=m;i++)
	{
		x++;
		while(x<=n&&l[x]<r[i]-ans) x++;
		if(l[x]>r[i]+ans) return 0;
		if(x>n) return 0;
		
	}
	return 1;
}

signed main()
{
	freopen("gloves.in","r",stdin);
	freopen("gloves.out","w",stdout);
	n=read(),m=read();
	for(int i=1;i<=n;i++) l[i]=read();
	for(int i=1;i<=m;i++) r[i]=read();
	if(m>n) swap(n,m),swap(l,r);
	sort(l+1,l+1+n),sort(r+1,r+1+m);
	int li=0,ri=1e9+10,ans=0;
	while(li<=ri)
	{
		int mid=(li+ri)>>1;
		if(check(mid))
		{
			ri=mid-1;
			ans=mid;
		}
		else li=mid+1;
	}
	printf("%lld",ans);
}

小 Z 的字符串(string)

DP,直接设 \(f[i][j][k][0/1/2]\) 表示目前填了 \(i\)\(0\)\(j\)\(1\)\(k\)\(2\) ,当前为 \(0/1/2\) ,转移也好说,以 \(0\) 举例。

\(f[i][j][k][0]=min(f[i-1][j][k][1],f[i-1][j][k][2])+abs(i+j+k-c[0][i])\)

其中 \(c[i][j]\) 表示 \(0/1/2\) 的第 \(j\) 个数。

由于这个移动在DP中是单向的,而不是正常模拟的双向,所以最后答案要除 \(2\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;

const int N=300;
char s[N];
int len;
int a[N],f[N][N][N][3];
int c[3][N],num[N];

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

int main()
{
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	
	scanf(" %s",s+1);
    len=strlen(s+1);
	for(int i=1;i<=len;i++)
	{
		a[i]=s[i]-'0';
		num[a[i]]++;
		c[a[i]][num[a[i]]]=i;
	}
	for(int i=0;i<=2;i++) if(num[i]>(len+1)/2) return printf("-1"),0;
	
	memset(f,0x3f,sizeof f);
	f[0][0][0][0]=f[0][0][0][1]=f[0][0][0][2]=0;
	for(int i=0;i<=num[0];i++)
	{
		for(int j=0;j<=num[1];j++)
		{
			for(int k=0;k<=num[2];k++)
			{
				int sum=i+j+k;
				if(i) f[i][j][k][0]=min(f[i-1][j][k][1],f[i-1][j][k][2])+abs(sum-c[0][i]);
				if(j) f[i][j][k][1]=min(f[i][j-1][k][0],f[i][j-1][k][2])+abs(sum-c[1][j]);
				if(k) f[i][j][k][2]=min(f[i][j][k-1][0],f[i][j][k-1][1])+abs(sum-c[2][k]);
				
			}
		}
	}
	int i=num[0],j=num[1],k=num[2];
	int ans=min({f[i][j][k][0],f[i][j][k][1],f[i][j][k][2]});
	printf("%d",ans/2);
}

一个真实的故事(truth)

由于 \(k\) 比较小,我们直接开线段树维护,那我们考虑如何去合并区间来得到答案,跟山海经那题一样的思路,首先根节点的答案肯定可以由左右两颗子树直接转移,问题主要出在两个区间合并中间的那段区间也会产生答案。

我们维护 \(k\) 的一个前驱和后继,合并肯定是根节点左儿子的后继和右儿子的前驱来合并,至于合并的方法,我们可以考虑莫队那样的移动用双指针来进行操作。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define int long long
const int N=1e5+107;
int n,k,m,a[N];
int ans[N],len[N];

#define lson (rt<<1)
#define rson (rt<<1|1)
struct lmy
{
	int pre[31],nxt[31];
	int ans;
	int l,r;
}tr[N<<2];

struct qx
{
	int pos,val;
}tmp[70];
void clear(){for(int i=1;i<=k;i++) tmp[i].pos=tmp[i].val=0;}
bool comp(qx a,qx b){return a.pos<b.pos;}

int read()
{
	int f=1,s=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){s=(s<<1)+(s<<3)+(ch^48);ch=getchar();}
	return f*s;
}

void pushup(int rt)
{
	for(int i=1;i<=k;i++) tr[rt].pre[i]=tr[rt].nxt[i]=0;
	tr[rt].ans=1e18;
	
	tr[rt].ans=min(tr[lson].ans,tr[rson].ans);
	
	for(int i=1;i<=k;i++) tr[rt].pre[i]=tr[lson].pre[i];
	for(int i=1;i<=k;i++) if(!tr[rt].pre[i]) tr[rt].pre[i]=tr[rson].pre[i];
	
	for(int i=1;i<=k;i++) tr[rt].nxt[i]=tr[rson].nxt[i];
	for(int i=1;i<=k;i++) if(!tr[rt].nxt[i]) tr[rt].nxt[i]=tr[lson].nxt[i];
	
	clear();int len=0;
	for(int i=1;i<=k;i++) 
	{
		if(tr[rson].pre[i]!=0) tmp[++len]={tr[rson].pre[i],i};
		if(tr[lson].nxt[i]!=0) tmp[++len]={tr[lson].nxt[i],i};
	}
	sort(tmp+1,tmp+1+len,comp);
	int j=0,sum=0,cnt[50]={},ans=1e18;
	for(int i=1;i<=len;i++)
	{
		while(j<len&&sum<k)
		{
			j++;
			if(cnt[tmp[j].val]<=0) sum++;
			cnt[tmp[j].val]++;
		}
		if(sum<k) break;
		cnt[tmp[i].val]--;
		if(cnt[tmp[i].val]<=0) sum--;
		ans=min(ans,tmp[j].pos-tmp[i].pos+1);
	}
	tr[rt].ans=min(tr[rt].ans,ans);
}

void build(int rt,int l,int r)
{
	tr[rt].l=l,tr[rt].r=r;
	tr[rt].ans=1e18;
	if(l==r) 
	{
		tr[rt].pre[a[l]]=tr[rt].nxt[a[l]]=l;
		return ;
	}
	int mid=(l+r)>>1;
	build(lson,l,mid);
	build(rson,mid+1,r);
	pushup(rt);
}

void update(int rt,int pos,int val)
{
	if(tr[rt].l==tr[rt].r) 
	{
		tr[rt].pre[a[tr[rt].l]]=tr[rt].nxt[a[tr[rt].l]]=0;
		a[tr[rt].l]=val;
		tr[rt].pre[val]=tr[rt].nxt[val]=tr[rt].l;
		return ;
	}
	int mid=(tr[rt].l+tr[rt].r)>>1;
	if(pos<=mid) update(lson,pos,val);
	if(pos>mid) update(rson,pos,val);
	pushup(rt);
}

signed main()
{
	freopen("truth.in","r",stdin);
	freopen("truth.out","w",stdout);
	n=read(),k=read(),m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++)
	{
		int op=read();
		if(op==1) 
		{
			int pos=read(),val=read();
			update(1,pos,val);
		}
		else 
		{
			if(tr[1].ans==1e18) printf("-1\n");
			else printf("%lld\n",tr[1].ans);
		}
	}
}
posted @ 2024-10-12 20:36  zhengchenxi  阅读(16)  评论(0编辑  收藏  举报