暑假集训CSP提高模拟2

A.活动投票

主元素问题,用摩尔投票

#include<bits/stdc++.h>
using namespace std;
int n,a=-1,acnt,x;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",&x);
		if(acnt==0){
			a=x;
			acnt++;
		}
		else if(a==x){
			acnt++;
		}
		else{
			acnt--;
			if(acnt==0) a=-1;
		}
	}
	printf("%d",a);
}

B.序列

mei ting dong

C.Legacy

赛时打的时候想可能是 DIJ 会假,结果 DIJ 没假,但是建图意料之中的 T 了.

这道题的建图非常有特色,区间建图的话,虽然不知道怎么建,但是确实是应该想到线段树.

考虑建分层图,然后把目标区间用线段树拆成节点,因为目标区间可以是出边也可以是入边,所以分两层,然后在每层叶节点连边权为零的边,这样建图复杂度只有 log. 然后在树上跑 DIJ 就行了

关于 SPFA

  • 它死了

D.DP搬运工1

题解里说了,考虑设一个 fi,j,k 做 DP

但是我觉得题解里的说法不是很清楚,我对这个 DP 的理解是,假设我们现在有一堆格子需要填,用 i 来表示已经填入的数字数量,j 来表示目前的空缺总数(注意这里的 “空缺”,一个空格组成的联通块称作一个 “空缺”,这么定义是因为比较好转移),k 用来表示当前填入数字后的 max 总和

我们考虑以下情况(用 S 表示一个空缺,x,y 分别表示数字)

xSy

在我们将 S 填上的时候,假设我们每次都填一个更大的值,显然,填上之前的和是 a+b,之后的和是 2k,显然这么做会很麻烦,因为我们在填的时候还要考虑先把 a+b 减掉. 因此我们可以想到,与其先加上 a+b 再减掉,还不如直接不统计这个 a+b 了,因此我们直接在最后填上的时候再加上边界的贡献,这样就会比较简单.

因此,枚举五种情况:填在外面并合并,填在外面并新建连通块,填在里面并左右合并,填在里面并左或右合并,填在里面并新建联通块. 可以分别得到连通块贡献 0,1,1,0,1max 求和贡献 i,0,2i,i,0,并且注意情况 1,2,4 是左右均可的合并方式,方案数贡献需要乘二

初始化 f[1][0][0]=1

fi,j,k+i=2fi1,j,k

fi,j,k=2fi1,j1,k

fi,j,k=fi1,j1,k

fi,j,k+i=2fi1,j,k

fi,j,k+2i=fi1,j+1,k

统计答案 f[n][0][i]

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int p=998244353;
int f[51][51][2501];
int n,kk;
signed main(){
	cin>>n>>kk;
	f[1][0][0]=1;
	for(int i=2;i<=n;++i){
		for(int j=0;j<=n-i+1;++j){
			for(int k=0;k<=kk;++k){
				if(!f[i-1][j][k]) continue;
				if(j){
					f[i][j][k+i]+=2*f[i-1][j][k]*j%p;
					f[i][j+1][k]+=f[i-1][j][k]*j%p;
					f[i][j-1][k+2*i]+=f[i-1][j][k]*j%p;
				}
				f[i][j][k+i]+=2*f[i-1][j][k]%p;
				f[i][j+1][k]+=2*f[i-1][j][k]%p;
			}
		}
	}
	int ans=0;
	for(int i=0;i<=kk;++i){
		ans+=f[n][0][i];
		ans%=p;
	}
	cout<<ans;
}
posted @   HaneDaniko  阅读(35)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示