8.15HL day5 NOIP模拟赛

T1手推打表 \(20min\) 毫无发现

T2 \(5min\) 思路+ \(30min\) 代码+\(2h\)调试(状态转移写错+topo入度写错)

T3,T4 \(20min\) 观察,并打了T3的暴力(太急了st板子打错了)

回去看T1并打了暴力(模数打成\(98244353\)了),打完发现杨辉三角,此时还剩 \(10min\)

拼手速失败,\(0+100+0+0=100pts\) 被自己气笑了

以下为T1-T3题解

T1

image

观察一下每个 \(a_1\) 在每个前缀和中的每一项的出现次数

即定义 \(f_{i,j}\) 为第 \(i\) 重前缀和的第 \(j\) 个数中 \(a_1\) 的出现次数,得到下表

image

转移方程为 \(f_{i,j}=f_{i-1,j}+f_{i,j-1}\) ,是不是有点眼熟?

画几条分割线就明白了

image

看到了吗?杨辉三角形

通过杨辉三角的通项公式,即第 \(i\) 行的第 \(j\) 个为\(C_{i-1}^{j-1}\)

例如我们要求第五重前缀和,则第一位至第四位系数依次为:

\(C_{4}^{0}=1\)\(C_{5}^{1}=5\)\(C_{6}^{2}=15\)\(C_{7}^{3}=35\)

\(a_2,a_3,...\) 这些的系数只要把整个 \(a_1\) 系数表对应的进行向右位移即可

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=5005;
const int maxm=1e7+maxn+5;
const int mod=998244353;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
LL fac[maxm];
int a[maxn],g[maxn];
LL ans[maxn];
int n,m;
LL fpow(LL x,int y){
	LL res=1;
	while(y){
		if(y&1) res=res*x%mod;
		x=x*x%mod,y>>=1;
	}
	return res;
}
LL C(int x,int y){
	return fac[x]*fpow(fac[x-y],mod-2)%mod*fpow(fac[y],mod-2)%mod;
}
int main(){
	//freopen("prefix.in","r",stdin);
	//freopen("prefix.out","w",stdout);
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	fac[0]=1;
	for(int i=1;i<=1e7+maxn;i++) fac[i]=fac[i-1]*i%mod;
	for(int i=1;i<=n;i++) g[i]=C(m+i-2,i-1);
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			ans[j]=(ans[j]+1ll*g[j-i+1]*a[i]%mod)%mod;
		}
	}
	for(int i=1;i<=n;i++){
		printf("%lld ",ans[i]);
	}
	return 0;
}
//^o^

T2

image

转化问题,我们只要求路径上的严格次大值即可

常见的思路,先 \(tarjan\) 缩点然后跑拓扑排序,这里讲一下状态转移

维护两个 \(dp\) 数组,\(dp1\) 到这一点的所有路径中最大值,\(dp2\) 到这一点的最大合法次大值

为什么不直接最大值和次大值呢?因为这样的话有可能最大值和次大值不在同一条路径上,就不对了

转移:首先 \(dp2_v=max(dp2_v,dp2_u)\)\(dp1_v=max(dp1_v,dp1_u)\)

如果发现当前点权大于 \(dp1_u\) ,那么可以将 \(dp1_u\) 作为合法次大值

如果发现当前点权小于 \(dp1_u\) ,则可以将当前点权作为合法次大值

得到状态转移代码:

dp1[v]=max(dp1[v],dp1[u]);
dp2[v]=max(dp2[v],dp2[u]);
if(mx[v]>dp1[u]) dp2[v]=max(dp2[v],dp1[u]);
if(mx[v]<dp1[u]) dp2[v]=max(dp2[v],mx[v]);
#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=2e5+5,maxm=1e6+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
class mmap{
public:
	int head[maxn],nxt[maxm],e[maxm];
	int mp_cnt;
	mmap(){
		memset(head,-1,sizeof(head));
		mp_cnt=-1;
	}
	void add_edge(int u,int v){
		e[++mp_cnt]=v;
		nxt[mp_cnt]=head[u];
		head[u]=mp_cnt;
	}
}mp1,mp2;
int n,m,q,s;
int a[maxn];
int mx[maxn],smx[maxn];
int dp1[maxn],dp2[maxn];
//dp1到这一点的最大值,dp2到这一点的最大次大值
int in[maxn];
int dfn[maxn],low[maxn],g[maxn];
bool ins[maxn],vis[maxn];
stack<int> st;
int cnt=0,tot=0;
void update(int u,int x){
	if(mx[u]<x) smx[u]=mx[u],mx[u]=x;
	else if(smx[u]<x&&x<mx[u]) smx[u]=x;
}
void tarjan(int u){
	dfn[u]=low[u]=++cnt;
	ins[u]=1,st.push(u);
	for(int i=mp1.head[u];~i;i=mp1.nxt[i]){
		int v=mp1.e[i];
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}
		else if(ins[v]){
			low[u]=min(low[u],dfn[v]);
		}
	}
	if(low[u]==dfn[u]){
		++tot;
		int now=-1;
		while(now!=u){
			now=st.top(),st.pop();
			ins[now]=0,g[now]=tot;
			update(tot,a[now]);
		}
	}
}
void dfs(int u){
	vis[u]=1;
	dp1[u]=mx[u],dp2[u]=smx[u];
	for(int i=mp2.head[u];~i;i=mp2.nxt[i]){
		int v=mp2.e[i];
		++in[v];
		if(vis[v]) continue;
		dfs(v);
	}
}
void topo(){
	queue<int> q;
	q.push(g[s]);
	while(!q.empty()){
		int u=q.front();
		q.pop();
		for(int i=mp2.head[u];~i;i=mp2.nxt[i]){
			int v=mp2.e[i];
			--in[v];
			dp1[v]=max(dp1[v],dp1[u]);
			dp2[v]=max(dp2[v],dp2[u]);
			if(mx[v]>dp1[u]) dp2[v]=max(dp2[v],dp1[u]);
			if(mx[v]<dp1[u]) dp2[v]=max(dp2[v],mx[v]);
			if(!in[v]){
				q.push(v);
			}
		}
	}
}
int main(){
	//freopen("mod.in","r",stdin);
	//freopen("mod.out","w",stdout);
	read(n),read(m),read(q),read(s);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	int u,v;
	for(int i=1;i<=m;i++){
		read(u),read(v);
		mp1.add_edge(u,v);
	}
	memset(mx,-1,sizeof(mx));
	memset(smx,-1,sizeof(smx));
	for(int i=1;i<=n;i++){
		if(!dfn[i]){
			tarjan(i);
		}
	}
	for(int i=1;i<=n;i++){
		u=i;
		for(int j=mp1.head[u];~j;j=mp1.nxt[j]){
			v=mp1.e[j];
			if(g[u]!=g[v]){
				mp2.add_edge(g[u],g[v]);
			}
		}
	}
	dfs(g[s]);
	topo();
	while(q--){
		read(u);
		u=g[u];
		if(!vis[u]) printf("-1\n");
		else if(dp2[u]==-1) printf("0\n");
		else printf("%d\n",dp2[u]);
	}
	return 0;
}
//^o^

T3

image

计数题,设计一个 \(dp_i\) 表示以第 \(i\) 位结尾的合法数列个数

然后分别求出每个 \(a_i\) \(b_i\) 的靠左第一个大于它的,靠右第一个大于等于它的(防止重复)

然后进行区间统计,详见代码

注意每个数字也可以去继承它前面的最大值,找到满足 \(max(a_i,b_i)<max(a_j,b_j)\) 的最大 \(j\),然后加上 \(dp_j\)

#include<bits/stdc++.h>
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
const int maxn=1e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=x*10+c-48;
	x=(f ? -x : x);
	return;
}
int n;
int a[maxn],b[maxn],c[maxn];
int la[maxn],ra[maxn],lb[maxn],rb[maxn],kk[maxn];
int t1[maxn],t2[maxn];
LL dp[maxn];
void getl(int* p,int* ans){
	stack<int> st;
	for(int i=1;i<=n;i++){
		while((!st.empty())&&p[st.top()]<=p[i]) st.pop();
		if(!st.empty()) ans[i]=st.top()+1;
		else ans[i]=1;
		st.push(i);
	}
}
void getr(int* p,int* ans){
	stack<int> st;
	for(int i=1;i<=n;i++){
		while((!st.empty())&&p[st.top()]<=p[i]){
			ans[st.top()]=i-1;
			st.pop();
		}
		st.push(i);
	}
	while(!st.empty()){
		ans[st.top()]=n;
		st.pop();
	}
}
int main(){
	//freopen("seqmax.in","r",stdin);
	//freopen("seqmax.out","w",stdout);
	read(n);
	for(int i=1;i<=n;i++) read(a[i]);
	for(int i=1;i<=n;i++) read(b[i]);
	for(int i=1;i<=n;i++) c[i]=max(a[i],b[i]);
	getl(a,la),getr(a,ra),getl(b,lb),getr(b,rb),getl(c,kk);
	LL ans=0;
	for(int i=1;i<=n;i++){
		t1[a[i]]=t2[b[i]]=i;
		if(a[i]>=b[i]){
			int j=t2[a[i]];
			if(j>=la[i]&&rb[j]>=i) dp[i]+=j-max(la[i],lb[j])+1;
		}
		else{
			int j=t1[b[i]];
			if(j>=lb[i]&&ra[j]>=i) dp[i]+=j-max(lb[i],la[j])+1;
		}
		dp[i]+=dp[kk[i]-1];
		ans+=dp[i];
	}
	printf("%lld",ans);
	return 0;
}
//^o^
posted @ 2025-08-15 18:01  huangems  阅读(17)  评论(0)    收藏  举报