2023.9.23测试

9.23 NOIP模拟赛

T1 雷老师的正偏态分布

给出一组数据 a1,a2,,an,统计 a 的所有子集中平均数小于中位数的子集数量(子集大小为奇数)
1n1001V800

容易想到排序,然后枚举中位数 x 和它的左右两边选取数的数量 j。设左右两边选取的数的和分别是 s1,s2,则需满足 s1+s2<2jx

因为值域很小,可以左右两边背包,设 fj,k/gj,k 表示左边/右边选 j 个数和为 k 的方案数,S=2jx,则贡献为 fj,k×t=1Sk1gj,t,做前缀和可以做到 O(n3V) 加上枚举的复杂度为 O(n4V),考场上这样写拿到 70

其实发现对于左边来说,每次移动相当于加上一个数,对于右边来说相当于减去一个数(即反悔背包),这样可以做到 O(n3V)

code
#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
#define LL long long
using namespace std;

const int N=110,V=810;
const int MOD=998244353;

int n,v,a[N],t,s;
int f[N>>1][N*V],g[N>>1][N*V][2],sum[N>>1][N*V],ans;

void prework()
{
	t=n/2;  s=n*v;
	f[0][0]=g[0][0][0]=1;
	for(int i=1; i<=n; i++)
		for(int j=t; j>=1; j--)
			for(int k=s; k>=0; k--)
				(g[j][k][0]+=g[j-1][k-a[i]][0])%=MOD;
}

void solve1(int i)
{
	if(i<1)
		return;
	for(int j=t; j>=1; j--)
		for(int k=s; k>=a[i]; k--)
			(f[j][k]+=f[j-1][k-a[i]])%=MOD;
}

void solve2(int i)
{
	if(i<1)
		return;
	g[0][0][1]=1;
	for(int j=1; j<=t; j++)
	{
		for(int k=0; k<=s; k++)
		{
			if(k<a[i])
				g[j][k][1]=g[j][k][0];
			else
				g[j][k][1]=((g[j][k][0]-g[j-1][k-a[i]][1])%MOD+MOD)%MOD;
				g[j][k][0]=g[j][k][1];
				sum[j][k]=(sum[j][k-1]+g[j][k][0])%MOD;
		}
	}		
}

int main()
{
//	freopen("end010.in","r",stdin);
//	freopen("a.out","w",stdout);
	
	scanf("%d%d",&n,&v);
	for(int i=1; i<=n; i++)
		scanf("%d",&a[i]);
		
	sort(a+1,a+1+n);
	
	prework();
	
	for(int i=1; i<=n; i++)
	{
		solve1(i-1);  solve2(i);
		
		for(int j=1; j<=min(i-1,n-i); j++)
			for(int k=1,S=j*2*a[i]; k<S; k++)
				(ans+=1LL*f[j][k]*sum[j][S-k-1]%MOD)%=MOD;
	}
	
	printf("%d",ans);

	return 0;
}

T2 假期计划Ⅱ

P8906 [USACO22DEC] Breakdown P

有点像 CSP-2022 T1 加强版

首先删边转加边,经典套路

之后也是经典的折半搜,设 hi 表示 1k 经过 k 条边的最短路,其中 k4。这里只考虑 k=4

考虑维护任意两点间最短路 fi,j。加边时只会对 i=uj=v 造成贡献,复杂度 O(n)。对于 hi,枚举中转站 j,计算 f1,j+fj,i。注意到只有 i/j=u/v 时会产生贡献度,复杂度 O(n)

注意特判 u=1 的情况,总复杂度 O(n3)

code
#include<bits/stdc++.h>
#pragma GCC optimize(3,"Ofast","inline")
using namespace std;

const int N=310,M=90010,INF=1e9;

int n,k,m,ans[M];
int e[N][N],u[M],v[M];

struct GR
{
	int k,st,e[N][N],f[N][N],h[N];
	
	void init(int _k,int _st)
	{
		k=_k;  st=_st;
		memset(e,0x3f,sizeof(e));
		memset(f,0x3f,sizeof(f));
		memset(h,0x3f,sizeof(h));
		if(!k)
			h[st]=0;
	}
	
	void add(int x,int y,int z)
	{
		e[x][y]=z;
		if(!k)
			return;
		if(k==1)
		{
			if(x==st)
				h[y]=z;
			return;
		}
		
		for(int i=1; i<=n; i++)
		{
			f[i][y]=min(f[i][y],e[i][x]+z);
			f[x][i]=min(f[x][i],z+e[y][i]);
		}
		
		if(k==2)
		{
			for(int i=1; i<=n; i++)
				h[i]=f[st][i];
			return;
		}
		
		auto p=(k==3)? e:f;
		if(x==st)
		{
			for(int i=1; i<=n; i++)
				for(int j=1; j<=n; j++)
					h[j]=min(h[j],f[x][i]+p[i][j]);
		}
		for(int i=1; i<=n; i++)
		{
			h[i]=min(h[i],f[st][x]+p[x][i]);
			h[i]=min(h[i],f[st][y]+p[y][i]);
			h[x]=min(h[x],f[st][i]+p[i][x]);
			h[y]=min(h[y],f[st][i]+p[i][y]);
		}
	}
}k1,k2;

int main()
{
	memset(ans,0x3f,sizeof(ans));
	
	scanf("%d%d",&n,&k);
	m=n*n;
	for(int i=1; i<=n; i++)
		for(int j=1; j<=n; j++)
			scanf("%d",&e[i][j]);
	for(int i=1; i<=m; i++)
		scanf("%d%d",&u[i],&v[i]);
		
	int lcnt=k/2,rcnt=k-lcnt;
	k1.init(lcnt,1);  k2.init(rcnt,n);
		
	for(int i=m; i>=1; i--)
	{
		for(int j=1; j<=n; j++)
			ans[i]=min(ans[i],k1.h[j]+k2.h[j]);
		k1.add(u[i],v[i],e[u[i]][v[i]]);
		k2.add(v[i],u[i],e[u[i]][v[i]]);
	}
	
	for(int i=1; i<=m; i++)
	{
		if(ans[i]>=INF)
			printf("-1\n");
		else
			printf("%d\n",ans[i]);
	}

	return 0;
}

T3 永不加班

P8476 「GLR-R3」惊蛰

很蓝的啦

考虑朴素的 DP,设 gi,j 表示 bi=j 时的最小答案,则有转移 gi,j=minkjgi1,k+f(j,ai),复杂度 O(nV)

gi1 做一遍后缀 min,然后令 gi,jgi1,j+f(j,ai)。后缀 minf 优秀的性质使得我们可以快速维护。具体的,对 gi1 做后缀 min 之后,gi1 具有单调性,gi1,jgi1,j+1。而 f(j,ai) 是关于 j 的分段函数,当 j<ai 时,相当于对 gi1,j 区间加 C。当 jai 时相当于对 gi1,j 加上 jai。两部分分别具有单调性,所以操作结束后 gi,jai 为分割线变成两段关于 j 具有单调性的序列

因此,操作时只需线段树二分出 <ai 的位置中最后一个使得 gi,j>gi,ai 的位置 j,并将 gi,jgi,ai1 区间赋值成 gi,ai

仔细思考一下可以得知 bi,存在 aj=bi。将 a 离散化,设 gi,j,表示 bi=aj 的最小答案,需要支持下列操作:

  • 区间加法

  • 区间赋值

  • 线段树二分出 <p 的位置最后一个 >v 的位置 q,保证 <p 的位置具有单调性

线段树维护即可

code
#include<bits/stdc++.h>
#define LL long long
#define lc(p) p<<1
#define rc(p) p<<1|1 
using namespace std;

const int N=1e6+10;

int n,m,C;
int a[N],b[N];

struct Seg
{
	LL dat,fu,ad1,ad2;
	#define dat(x) tree[x].dat
	#define fu(x) tree[x].fu
	#define ad1(x) tree[x].ad1
	#define ad2(x) tree[x].ad2
	
	void add(LL v1,LL v2,int l)
	{
		dat+=v1+1LL*v2*b[l];  
		ad1+=v1;  ad2+=v2;
	}
	
	void cov(LL v)
	{
		dat=fu=v;
		ad1=ad2=0;
	}
}tree[N<<2];

void pushup(int p)
{
	dat(p)=max(dat(lc(p)),dat(rc(p)));
}

void spread(int p,int l,int r)
{
	int mid=(l+r)>>1;
	if(fu(p)!=-1)
	{
		tree[lc(p)].cov(fu(p));
		tree[rc(p)].cov(fu(p));
		fu(p)=-1;
	}
	if(ad1(p) || ad2(p))
	{
		tree[lc(p)].add(ad1(p),ad2(p),mid);
		tree[rc(p)].add(ad1(p),ad2(p),r);
		ad1(p)=ad2(p)=0;
	}
}

void build(int p,int l,int r)
{
	fu(p)=-1;
	dat(p)=ad1(p)=ad2(p)=0;
	if(l==r)
		return;
	int mid=(l+r)>>1;
	build(lc(p),l,mid);
	build(rc(p),mid+1,r);
	pushup(p);
}

void add(int p,int l,int r,int ql,int qr,LL v1,LL v2)
{
	if(ql<=l && qr>=r)
	{
		tree[p].add(v1,v2,r);
		return;
	}
	spread(p,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)
		add(lc(p),l,mid,ql,qr,v1,v2);
	if(qr>mid)
		add(rc(p),mid+1,r,ql,qr,v1,v2);
	pushup(p);
}

void cov(int p,int l,int r,int ql,int qr,LL v)
{
	if(ql<=l && qr>=r)
	{
		tree[p].cov(v);
		return;
	}
	spread(p,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid)
		cov(lc(p),l,mid,ql,qr,v);
	if(qr>mid)
		cov(rc(p),mid+1,r,ql,qr,v);
	pushup(p); 
}

LL ask(int p,int l,int r,int pos)
{
	if(l==r)
		return dat(p);
	spread(p,l,r);
	int mid=(l+r)>>1;
	if(pos<=mid)
		return ask(lc(p),l,mid,pos);
	return ask(rc(p),mid+1,r,pos);
}

int find(int p,int l,int r,int ql,int qr,LL v)
{
	if(dat(p)<v)
		return -1;
	if(l==r)
		return l;
	spread(p,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid && dat(lc(p))>=v)
		return find(lc(p),l,mid,ql,qr,v);
	if(qr>mid)
		return find(rc(p),mid+1,r,ql,qr,v);
}

int main()
{
	scanf("%d%d",&n,&C);
	for(int i=1; i<=n; i++)
	{
		scanf("%d",&a[i]);
		b[i]=a[i];
	}
	
	sort(b+1,b+1+n);
	m=unique(b+1,b+1+n)-(b+1);
	
	build(1,1,m);
	
	for(int i=1; i<=n; i++)
	{
		int x=lower_bound(b+1,b+1+m,a[i])-b;
		if(x+1<=m)
			add(1,1,m,x+1,m,-a[i],1);
		if(x-1>=1)
		{
			add(1,1,m,1,x-1,C,0); 
			LL tmp=ask(1,1,m,x);
			int pos=find(1,1,m,1,x-1,tmp);
			if(pos!=-1)
				cov(1,1,m,pos,x-1,tmp);
		}
	} 
	
	printf("%lld",ask(1,1,m,1));

	return 0;
}

70+0+25+0=95rk19

posted @   xishanmeigao  阅读(13)  评论(0编辑  收藏  举报
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示