2025省选模拟14

2025省选模拟14

题目来源: 2025多校冲刺省选模拟赛16

T1 P1022. 世界⼤战 15pts

  • 原题: luogu P7294 [USACO21JAN] Minimum Cost Paths P

  • 部分分

    • 15ptsO(n2) 预处理 O(1) 查询。
    点击查看代码
    ll f[2010][2010],c[2010];
    int main()
    {
    #define Isaac
    #ifdef Isaac
    	freopen("worldwar.in","r",stdin);
    	freopen("worldwar.out","w",stdout);
    #endif
    	ll n,m,q,x,y,i,j;
    	scanf("%lld%lld",&n,&m);
    	for(i=1;i<=m;i++)   scanf("%lld",&c[i]);
    	memset(f,0x3f,sizeof(f));
    	f[1][1]=0;
    	for(i=1;i<=n;i++)
    	{
    		for(j=1;j<=m;j++)
    		{
    			f[i+1][j]=min(f[i+1][j],f[i][j]+c[j]);
    			f[i][j+1]=min(f[i][j+1],f[i][j]+i*i);
    		}
    	}
    	scanf("%lld",&q);
    	for(i=1;i<=q;i++)
    	{
    		scanf("%lld%lld",&x,&y);
    		printf("%lld\n",f[x][y]);
    	}
    	return 0;
    }
    
  • 正解

    • 考虑从 (1,1)(x,y) 中间经过的若干中转点 (x1,y1),(x2,y2),,(xk,yk) 的贡献。
    • 对于一条竖-横-竖的路径 (x1,y1)(x,y1)(x,y2)(x2,y2) ,其代价为 x2(y2y1)+cy1(xx1)+cy2(x2x)=(y2y1)x2+(cy2cy1)x+cy2x2cy1x1 ,其最小值在 cy2cy12(y2y1) 时取到,具体取值因要求为正整数所以取 max(min(round(cy2cy12(y2y1)),1),n)
    • opt(y1,y2) 等于上式。根据调整法,升序枚举 y 的同时单调栈维护单调递增的 opt(yi1,yi) ,预处理前缀和后二分得到答案。
    • 特判 y=1 时的转移。
    点击查看代码
    ll c[200010],sum[200010],ans[200010],s[200010],opt[200010],n,top=0;
    vector<pair<ll,ll> >q[200010];
    ll get_opt(ll u,ll v)
    {
    	return min(max((ll)round((c[v]-c[u])/(v-u)/2.0),1ll),n);
    }
    int main()
    {
    // #define Isaac
    #ifdef Isaac
    	freopen("worldwar.in","r",stdin);
    	freopen("worldwar.out","w",stdout);
    #endif
    	ll m,k,x,y,pos,i,j;
    	scanf("%lld%lld",&n,&m);
    	for(i=1;i<=m;i++)  scanf("%lld",&c[i]);
    	scanf("%lld",&k);
    	for(i=1;i<=k;i++)
    	{
    		scanf("%lld%lld",&x,&y);
    		if(y==1)  ans[i]=c[1]*(x-1);
    		else  q[y].push_back(make_pair(x,i));
    	}
    	top=s[1]=opt[1]=1;
    	for(i=2;i<=m;i++)
    	{
    		while(top>=2&&opt[top]>=get_opt(s[top],i))  top--;
    		pos=get_opt(s[top],i);
    		top++;  s[top]=i;  opt[top]=pos;
    		sum[top]=sum[top-1]+(pos-opt[top-1])*c[s[top-1]]+(i-s[top-1])*pos*pos;
    		for(j=0;j<q[i].size();j++)
    		{
    			pos=upper_bound(opt+1,opt+1+top,q[i][j].first)-opt-1;
    			ans[q[i][j].second]=sum[pos]+(q[i][j].first-opt[pos])*c[s[pos]]+(i-s[pos])*q[i][j].first*q[i][j].first;
    		}
    	}
    	for(i=1;i<=k;i++)  printf("%lld\n",ans[i]);
    	return 0;
    }
    

T2 P1023. 跳⽔ 25pts

  • 原题: luogu P3549 [POI 2013] MUL-Multidrink

  • 部分分

    • Subtask1 :枚举全排列。
    • Subtask2 :从 1 每次步长为 1 往下跳链直至 n 的父亲, n 以下的部分则步长为 2 地跳,特判链底。
    • Subtask4 :输出 1n

    因数据过水,代码实测可以通过 Subtask3 中完全二叉树的部分分。

    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[4000010];
    int head[2000010],du[2000010],f[2000010],fa[2000010],siz[2000010],dep[2000010],son[2000010],top[2000010],a[2000010],cnt=0;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    void dfs1(int x,int father)
    {
    	siz[x]=1;
    	fa[x]=father;
    	dep[x]=dep[father]+1;
    	f[dep[x]]=x;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=father)
    		{
    			dfs1(e[i].to,x);
    			siz[x]+=siz[e[i].to];
    			son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x];
    		}
    	}
    }
    void dfs2(int x,int id)
    {
    	top[x]=id;
    	if(son[x]!=0)
    	{
    		dfs2(son[x],id);
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa[x]&&e[i].to!=son[x])  dfs2(e[i].to,e[i].to);
    	}
    }
    int lca(int u,int v)
    {
    	while(top[u]!=top[v])
    	{
    		if(dep[top[u]]>dep[top[v]])  u=fa[top[u]];
    		else  v=fa[top[v]];
    	}
    	return dep[u]<dep[v]?u:v;
    }
    int get_dis(int x,int y)
    {
    	return dep[x]+dep[y]-2*dep[lca(x,y)];
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
    	freopen("jump.in","r",stdin);
    	freopen("jump.out","w",stdout);
    #endif
    	int n,u,v,flag=1,sum=0,i;
    	scanf("%d",&n);
    	for(i=1;i<=n-1;i++)
    	{
    		scanf("%d%d",&u,&v);
    		du[u]++;  du[v]++;
    		add(u,v);  add(v,u);
    	}
    	dfs1(1,0);  dfs2(1,1);
    	if(n<=12||du[1]==n-1)
    	{
    		for(i=1;i<=n;i++)  a[i]=i;
    		do
    		{
    			flag=1;
    			for(i=2;i<=n&&flag==1;i++)  flag&=(get_dis(a[i-1],a[i])<=2);
    			if(flag==1)
    			{
    				for(i=1;i<=n;i++)  printf("%d\n",a[i]);
    				return 0;
    			}
    		}while(next_permutation(a+2,a+n));
    		printf("quake!\n");
    	}
    	else
    	{
    		sum=0;  flag=1;
    		for(i=1;i<=n;i++) 
    		{
    			sum+=(du[i]==1);
    			flag&=(du[i]<=2);
    		} 
    		if(sum==2&&flag==1)
    		{
    			for(i=1;i<=dep[n]-1;i++)  printf("%d\n",f[i]);
    			for(i=dep[n]+1;i<=n;i+=2)  printf("%d\n",f[i]);
    			if(i==n+1)  printf("%d\n",f[n]);
    			for(i=n-2;i>=dep[n];i-=2)  printf("%d\n",f[i]);
    		}
    		else  printf("quake!\n");
    	}
    	return 0;
    }
    
  • 正解

    • 考虑将 1n 这条根链上的点提出来单独考虑。划分成的若干个部分相互独立。
    • fx,0/1/2/3 分别表示能否 xx 出/ xx 的儿子出/ x 的儿子进 x 的儿子出/ x 的儿子进 x 出。特别地,当儿子中只有一个非叶子节点时,令 fx,2=fx,1
      • xx 出,要求 x 必须是叶子节点。
      • xx 的儿子出,要求先到达 x ,然后到达儿子中的非叶子节点(如果有则必须只能有一个),最后跳到叶子节点。
      • x 的儿子进 x 的儿子出,在判掉必须有两个非叶子节点后,要求先到达第一个非叶子节点,然后到达 x ,接着到达第二个非叶子节点,最后跳到叶子节点。
      • x 的儿子进 x 出,要求先到达儿子中的叶子节点,再到达非叶子节点,最后跳到 x
    • fx,1fx,3 本质相同,只是构造方案时搜索顺序的差别,故可以合并成一种情况。
    • gx,0/1,x(1n) 分别表示 x 的子树遍历完后能否停在 x / x 的儿子。
      • x 出要求 fax 能出且 x 能出或 fax 的其他儿子能出且 xx 出。
        • fax 能出且 xx 出,从 fax 跳到 x 这个叶子节点。
        • fax 能出且 x 的儿子进 x 出,从 fax 跳到 x 能进的儿子,再跳到 x
        • fax 的儿子能出要求必须 xx 出,否则就跳不出来了。
      • x 的儿子出要求 fax 能出且 x 的儿子能出或 fax 的其他儿子能出且 xx 的儿子出。
        • fax 能出且 xx 的儿子出,从 fax 跳到 x ,再跳到能出的儿子。
        • fax 能出且 x 的儿子进 x 的儿子出,从 fax 跳到 x 能进的儿子,再跳到 x ,再跳到另一个能出的儿子。
        • fax 的儿子能出且 xx 的儿子出,从 fax 的儿子跳到 x ,再跳到能出的儿子。
    点击查看代码
    struct node
    {
    	int nxt,to;
    }e[4000010];
    int head[2000010],f[3][2000010],g[2][2000010],vis[2000010],n,cnt=0;
    vector<int>s,d;
    void add(int u,int v)
    {
    	cnt++;  e[cnt]=(node){head[u],v};  head[u]=cnt;
    }
    void dfs(int x,int fa)
    {
    	s.push_back(x);
    	if(x==n)  d=s;
    	for(int i=head[x];i!=0;i=e[i].nxt)  if(e[i].to!=fa)  dfs(e[i].to,x);
    	s.pop_back();
    }
    void dp(int x,int fa)
    {
    	int son=0,sum=0;
    	f[0][x]=f[1][x]=f[2][x]=1;
    	for(int i=head[x];i!=0;i=e[i].nxt)
    	{
    		if(e[i].to!=fa&&vis[e[i].to]==0)
    		{
    			son++;
    			dp(e[i].to,x);
    			sum+=(f[0][e[i].to]==0&&f[1][e[i].to]==1);
    			f[1][x]&=(f[0][e[i].to]|f[1][e[i].to]);
    			f[2][x]&=(f[0][e[i].to]|f[1][e[i].to]);
    		}
    	}
    	f[1][x]&=(sum<=1);  f[2][x]&=(sum<=2);
    	if(son==0)  f[1][x]=f[2][x]=0;
    	else  f[0][x]=0;
    }
    void print_f(int x,int op,int fa)
    {
    	if(op==0)  printf("%d\n",x);
    	if(op==1)
    	{
    		printf("%d\n",x);
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0)  print_f(e[i].to,3,x);
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1)  print_f(e[i].to,0,x);
    	}
    	if(op==2)
    	{
    		int sum=0;
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0)  sum++;
    		if(sum==1)  print_f(x,1,fa);
    		else
    		{
    			for(int i=head[x];i!=0;i=e[i].nxt)
    			{	
    				if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0)
    				{
    					if(sum==2)
    					{
    						print_f(e[i].to,1,x);
    						printf("%d\n",x);
    						sum++;
    					}
    					else  print_f(e[i].to,3,x);
    				}
    			}
    			for(int i=head[x];i!=0;i=e[i].nxt)
    				if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1)  print_f(e[i].to,0,x);
    		}
    	}
    	if(op==3)
    	{
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1)  print_f(e[i].to,0,x);
    		for(int i=head[x];i!=0;i=e[i].nxt)
    			if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0)  print_f(e[i].to,1,x);
    		printf("%d\n",x);
    	}
    }
    void print_g(int x,int op)
    {
    	if(x==0)  print_f(d[x],op,0);
    	else
    	{
    		if(op==0)
    		{
    			if(g[0][x-1]==1&&f[0][d[x]]==1)
    			{
    				print_g(x-1,0);  print_f(d[x],0,0);
    			}
    			else  if(g[0][x-1]==1&&f[1][d[x]]==1)
    			{
    				print_g(x-1,0);  print_f(d[x],3,0);
    			}
    			else  
    			{
    				print_g(x-1,1);  print_f(d[x],0,0);
    			}
    		}
    		else
    		{
    			if(g[0][x-1]==1&&f[1][d[x]]==1)
    			{
    				print_g(x-1,0);  print_f(d[x],1,0);
    			}
    			else  if(g[0][x-1]==1&&f[2][d[x]]==1)
    			{
    				print_g(x-1,0);  print_f(d[x],2,0);
    			}
    			else  
    			{
    				print_g(x-1,1);  print_f(d[x],1,0);
    			}
    		}
    	}
    }
    int main()
    {
    #define Isaac
    #ifdef Isaac
    	freopen("jump.in","r",stdin);
    	freopen("jump.out","w",stdout);
    #endif
    	int u,v,i;
    	scanf("%d",&n);
    	for(i=1;i<=n-1;i++)
    	{
    		scanf("%d%d",&u,&v);
    		add(u,v);  add(v,u);
    	}
    	dfs(1,0);
    	for(i=0;i<d.size();i++)  vis[d[i]]=1;
    	for(i=0;i<d.size();i++)  dp(d[i],0);
    	g[0][0]=f[0][1];  g[1][0]=f[1][1];
    	for(i=1;i<d.size();i++)
    	{
    		g[0][i]=(g[0][i-1]&(f[0][d[i]]|f[1][d[i]]))|(g[1][i-1]&f[0][d[i]]);
    		g[1][i]=(g[0][i-1]&(f[1][d[i]]|f[2][d[i]]))|(g[1][i-1]&f[1][d[i]]);
    	}
    	if(g[0][d.size()-1]==1)  print_g(d.size()-1,0);
    	else  printf("quake!\n");
    	return 0;
    }
    

T3 P1024. 蛤蟆的近亲 40pts

  • 先让 ain+10min 缩小值域。

  • 部分分

    • 40ptsO(n3) 预处理 O(1) 查询。
    点击查看代码
    int a[510],mex[510][510],w[510][510];
    uint s[510][510];
    bitset<100210>vis,f;
    int main()
    {
    #define Isaac
    #ifdef Isaac
    	freopen("frog.in","r",stdin);
    	freopen("frog.out","w",stdout);
    #endif
    	int n,m,i,j,k;
    	uint l,r,ans=0;
    	scanf("%d%d",&n,&m);
    	for(i=1;i<=n;i++)  
    	{
    		scanf("%d",&a[i]);
    		if(a[i]>=100010)  a[i]=100010;
    	}
    	for(i=1;i<=n;i++)
    	{
    		vis.set();
    		for(j=i;j<=n;j++)
    		{
    			vis[a[j]]=0;
    			mex[i][j]=vis._Find_first();
    		}
    	}
    	for(i=1;i<=n;i++)
    	{
    		f.reset();
    		for(j=i;j<=n;j++)
    		{
    			for(k=i;k<=j;k++)  f[mex[k][j]]=1;
    			w[i][j]=f.count();
    		}
    	}
    	for(i=n;i>=1;i--)
    	{
    		for(j=1;j<=n;j++)  s[i][j]=s[i+1][j]+s[i][j-1]-s[i+1][j-1]+w[i][j];
    	}
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%d",&l,&r);
    		l=(l^ans)%n+1;  r=(r^ans)%n+1;
    		if(l>r)  swap(l,r);
    		ans=s[l][r];
    		printf("%u\n",ans);
    	}
    	return 0;
    }
    
  • 正解

总结

  • T1 c[2m] 的部分分想简单了,以为只会存在两种走的方式。
  • T2
    • 为图方便把链底的特判和往下跳的过程合并在了一起 i=min(i+2,n) 然后忘退出了,挂了 15pts
    • 胡出来一个拆成入点和出点后跑路径匹配的有负圈的费用流(但全程流量为 1 )做法,没时间再写了。
posted @   hzoi_Shadow  阅读(35)  评论(3编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
扩大
缩小
点击右上角即可分享
微信分享提示