YL 模拟赛总结 1

Posted on 2024-03-03 18:30  _XOFqwq  阅读(2)  评论(0编辑  收藏  举报

Problem


T1

翻转反转后的字符串得到原串,与污染过的原串比较即可得到被污染的字符。

T2

dfs 枚举选 \(k\) 个数的方案,对于每种方案求能组成的数字个数,累加即可。

T3

恶♂趣♂味♂ \(dp\)

首先因为数据范围过大,所以先要进行离散化。

离散化的时候可以顺便求出 \(ps_i\)\(pt_i\)\(pg_i\)

分别表示在 \(i\) 之前的白 / 黑 / 黑白区间的最大右端点。

然后令 \(fs_i\) 为有以 \(i\) 为右端点的白区间时的方案数;

\(ft_i\) 为有以 \(i\) 为右端点的黑区间时的方案数;

\(fg_i\) 为有以 \(i\) 为右端点的白区间时的方案数;

\(ss_i\)\(st_i\)\(sg_i\) 分别是上面三者的前缀和。

接着考虑转移。

考虑白区间,它一定从黑 / 黑白区间转移而来,从黑转移而来的方案数即为 \(st_i-st_{pt_i-1}\)。黑白区间同理。

黑区间的转移路径与白区间是类似的。

考虑黑白区间,它可以从三个类型的区间转移而来,且还要加上当前区间的方案数,所以总方案数应为 \((fs_{i-1}+ft_{i-1}+fg_{i-1}) \times (2^{a_i-a_{i-1}+1}-2)\)

记得更新 \(ps_i\)\(pt_i\) 和三个前缀和。

这样转移过后,答案即为 \(fs_n+ft_n+fg_n\)

于是这道题就做完了,时间复杂度 \(O(n)\)

代码(还是挺长的):

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int MOD=1000000007;
int k,n,m;
int tot,a[400031];
int ls[400031],rs[400031],lt[400031],rt[400031];
int ps[400031],pt[400031];
int fs[400031],ft[400031],fg[400031];
int ss[400031],st[400031],sg[400031];

int qpow(int b,int p){
	if(p==0) return 1;
	if(p==1) return b%MOD;
	int t=qpow(b,p/2);
	if(p%2==0) return t*t%MOD;
	else return t*t%MOD*b%MOD;
}
int mod(int x){ return (x>=MOD?x-MOD:x); }

signed main(){
	cin>>k>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>ls[i]>>rs[i];
		a[++tot]=ls[i]-1,a[++tot]=rs[i];
	}
	for(int i=1;i<=m;i++){
		cin>>lt[i]>>rt[i];
		a[++tot]=lt[i]-1,a[++tot]=rt[i];
	}
	a[++tot]=0,a[++tot]=k;
	
	sort(a+1,a+tot+1);
	tot=unique(a+1,a+tot+1)-a-1;
	for(int i=1;i<=n;i++){
		int L=lower_bound(a+1,a+tot+1,ls[i]-1)-a;
		int R=lower_bound(a+1,a+tot+1,rs[i])-a;
		ps[R]=max(ps[R],L+1);
	}
	for(int i=1;i<=m;i++){
		int L=lower_bound(a+1,a+tot+1,lt[i]-1)-a;
		int R=lower_bound(a+1,a+tot+1,rt[i])-a;
		pt[R]=max(pt[R],L+1);
	}

	fg[1]=sg[1]=1,ps[1]=pt[1]=1;
	for(int i=2;i<=tot;i++){
		ps[i]=max(ps[i],ps[i-1]);
		pt[i]=max(pt[i],pt[i-1]);
		
		fs[i]+=mod(st[i-1]-st[pt[i]-1]+MOD);
		fs[i]+=mod(sg[i-1]-sg[pt[i]-1]+MOD);
		fs[i]=mod(fs[i]);
		
		ft[i]+=mod(ss[i-1]-ss[ps[i]-1]+MOD);
		ft[i]+=mod(sg[i-1]-sg[ps[i]-1]+MOD);
		ft[i]=mod(ft[i]);
		
		fg[i]=mod(fs[i-1]+mod(ft[i-1]+fg[i-1]))*(qpow(2,a[i]-a[i-1])-2+MOD)%MOD;
		
		ss[i]=mod(ss[i-1]+fs[i]);
		st[i]=mod(st[i-1]+ft[i]);
		sg[i]=mod(sg[i-1]+fg[i]);
	}
	
	cout<<mod(fs[tot]+mod(ft[tot]+fg[tot]));
	return 0;
}

T4

显然题目是要我们求最长路。跑一遍魔改 SPFA 即可。

但是本题数据很大,于是我们采用高精度的思想,将答案的每一位都存入数组中输出。

我们设定一个阈值 \(S=30\),下标 \(>S\) 的是整数部分,\(<S\) 的是小数部分。

然后将答案求出存入数组后,进行进位、四舍五入、再进位、去前导零后输出即可。

需要注意的是,我们 SPFA 求的是 \(1\) 到各点的最长路,但我们要求的是各点到 \(1\) 的最长路,因此我们需要将求出的每个最长路取反。

同时,因为方向反了,于是答案数组应当每次累加 \(a_i\)(即 \(\dfrac{a_i}{10^z} \times 10^z\))。

代码:

#include<bits/stdc++.h>
using namespace std;

int n,m,ans[100031];
int a[100031],dis[100031];
struct EdgeInfo{
	int to,w;
};
struct NodeMessage{
	int pos,tme;
};
vector<EdgeInfo> G[200031];
queue<NodeMessage> q;


int main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1,x,y,z;i<=m;i++){
		cin>>x>>y>>z;
		G[x].push_back((EdgeInfo){y,z});
	}

	memset(dis,-0x3f3f3f3f,sizeof(dis));
	q.push((NodeMessage){1,0}),dis[1]=0;
	while(!q.empty()){
		int x=q.front().pos; q.pop();
		for(auto i:G[x]){
			if(dis[x]+i.w>dis[i.to]){
				dis[i.to]=dis[x]+i.w;
				q.push((NodeMessage){i.to,dis[x]+i.w});
			}
		}
	}

	for(int i=1;i<=n;i++) dis[i]=-dis[i];
	for(int i=1;i<=n;i++)
		if(dis[i]+30>=0) ans[dis[i]+30]+=a[i];
	for(int i=1;i<=10040;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
	if(ans[23]>=5) ans[24]++;
	for(int i=1;i<=10040;i++) ans[i+1]+=ans[i]/10,ans[i]%=10;
	int now=100004;
	while(!ans[now]&&now>30) now--;

	for(int i=now;i>=30;i--) cout<<ans[i];
	cout<<'.';
	for(int i=29;i>=24;i--) cout<<ans[i];
  	return 0;
}