191105-模拟测试12

本文深入解析三道算法竞赛题目,涵盖模拟测试、次小生成树与字符串匹配问题,提供详细题解与代码实现,适合算法学习者及竞赛选手参考。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

191105-模拟测试12

T1 panwang

题目描述

T1

解析

首先,很显然,该题只有两种情况:
1,打完御符(前提是能打完),记为 a n s 1 ans1 ans1

2,根本不打御符,记为 a n s 2 ans2 ans2

最后的答案即是 m a x ( a n s 1 , a n s 2 ) max(ans1,ans2) max(ans1,ans2)

对于第一种情况,我们需要用能力值刚好比御符大或等于的兵符来打掉御符(很显然,这样是最优的)

然后就是坑点所在,考场上我直接判断御符打完后,就直接给 a n s 1 ans1 ans1加上了剩余所有兵符的能力值,显然这样是错的,因为我们忽略了对手手中能力值为负的兵符,若我们用剩余的兵符去攻击对手手中的兵符,显然我们会造成更大的伤害,同时,我们最后剩余的兵符中,不能给 a n s 1 ans1 ans1累加上我们手中能力值为负的兵符。

对于第二种情况就比较简单了,直接用我们手中最大的兵符,去打掉对手手中的最小的兵符,一定为最优的(至于正确性,感性理解,我就懒得证明了)

最后在算法上还有一点需要注意的,因为我们注意到能力值绝对值的最大值为100,因此我们开个大小为200的桶就可以轻松解决该题(而考场上没有考虑到,因此用了3个sort,会tle掉)

题解

#include<bits/stdc++.h>
#define M 207
using namespace std;
long long n,m,q,ans2,ans1,a[M],a1[M],b[M],c[M],c1[M];
long long read()
{
	int f=1;
	long long re=0;
	char ch;
	for(ch=getchar();(ch>'9'||ch<'0')&&ch!='-';ch=getchar());
	if(ch=='-'){
		f=-1;
		ch=getchar();
	}
	for(;ch>='0'&&ch<='9';ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
signed main()
{
	//freopen("panwang3.in","r",stdin);
	//freopen("panwang.out","w",stdout);
	long long x,y;
	n=read();
	m=read();
	q=read();
	for(int i=1;i<=n;i++){
		x=read();
		y=read();
		a[x+100]+=y;
		a1[x+100]+=y;
	}
	for(int i=1;i<=m;i++){
		x=read();
		y=read();
		b[x+100]+=y;
	}
	for(int i=1;i<=q;i++){
		x=read();
		y=read();
		c[x+100]+=y;
		c1[x+100]+=y;
	}
	for(int i=0,j=0;i<=200&&j<=200;)
	{
		while(!a[i]) i++;
		while(!b[j]) j++;
		if(i>200||j>200) break;
		while(i<j) i++;
		long long v=min(a[i],b[j]);
		a[i]-=v;
		b[j]-=v;
 	}
 	bool bj=0;
	for(int i=0;i<=200;i++)
		if(b[i]){
			bj=1;
			break;
		}
	if(!bj)
	{
		for(int i=200,j=0;i>=0&&j<=100;)
		{
			while(!a[i]) i--;
			while(!c[j]) j++;
			if(i<0||j>100) break;
			if(i<=j) break;
			long long v=min(a[i],c[j]);
			a[i]-=v;
			c[j]-=v;
			ans1+=(v*(i-j));
		}
 		for(int i=101;i<=200;i++)
 			ans1+=(a[i]*(i-100));
	}
	for(int i=200,j=0;i>=0&&j<=200;)
	{
		while(!a1[i]) i--;
		while(!c1[j]) j++;
		if(i<0||j>200) break;
		if(i<=j) break;
		long long v=min(a1[i],c1[j]);
		a1[i]-=v;
		c1[j]-=v;
		ans2+=(v*(i-j));
 	}
	printf("%lld",max(ans1,ans2));
	return 0;
}

T2 zhuhzu

题目描述

在这里插入图片描述

解析

考场上想出了次小生成树,但是码量较大,不想打了

话归正传,首先根据题意,我们知道最小的合法道路集合是所有合法的生成树中最小的一棵(不一定是原图的最小生成树,也可能是原图的次小生成树,由所给的 x x x值来定)

于是先求出原图的最小生成树,再分类讨论( s u m sum sum为原图最小生成树的大小, x x x为题目中所给定的值)

1, s u m > x sum>x sum>x 无解

2, s u m = x sum=x sum=x 于是次小生成树就会有两种,一种是大小也等于 x x x,另一种是大小大于 x x x的,大于 x x x的生成树随便染色即可,因此我们只需统计大小等于 x x x的数量 c n t 1 cnt1 cnt1

因此答案为: 2 m − 2 ∗ 2 m − ( n − 1 ) − c n t 1 2^m-2*2^{m-(n-1)-cnt1} 2m22m(n1)cnt1
(解释:因为 s u m = x sum=x sum=x,因此不存在小于 x x x的生成树,所以理论上所有边都可以随便染两种染色即 2 m 2^m 2m,但实际上要减去所有大小等于x的生成树上的边同色的情况,即 2 ∗ 2 m − ( n − 1 ) − c n t 1 2*2^{m-(n-1)-cnt1} 22m(n1)cnt1(为什么要 ∗ 2 *2 2,因为有两种颜色))

3, s u m < x sum<x sum<x 于是次小生成树会有三种,一种是大于 x x x的,同上,直接不管就好了,等于x的,可以替换的边数记为 c n t 2 cnt2 cnt2,小于x的,可以替换的边数记为 c n t 3 cnt3 cnt3

因此答案为: 2 ∗ ( 2 m − ( n − 1 ) − c n t 3 − 2 m − ( n − 1 ) − c n t 2 − c n t 3 ) 2*(2^{m-(n-1)-cnt3}-2^{m-(n-1)-cnt2-cnt3}) 2(2m(n1)cnt32m(n1)cnt2cnt3)
(解释:首先理论上除去大小小于 x x x的生成树上的边,其他边都可以随便染色,即 2 m − ( n − 1 ) − c n t 3 2^{m-(n-1)-cnt3} 2m(n1)cnt3,但实际上要减去大小等于 x x x的生成树上的边同色的请况,即 2 m − ( n − 1 ) − c n t 2 − c n t 3 2^{m-(n-1)-cnt2-cnt3} 2m(n1)cnt2cnt3,(为什么要 ∗ 2 *2 2,因为有两种颜色))

同时需要解释下次小生成树,这里说的次小生成树,实际上是和每条边所对应的,即 使该边强制出现在原图的最小生成树中的次小生成树(有点绕口,我也说不太清楚),而且该次小生成树与原图最小生成树有且只有一条边的差异(可以去做一道例题 严格次小生成树

其次,我们从原图最小生成树中替换下的边一定是,要替换上的边的两个端点,在原图最小生成树中所对应的链上的最大的边。

题解

#include<bits/stdc++.h>
#define M 200005
#define int long long
using namespace std;
const int mod=1e9+7;
int ans[M],to[M*2],first[M],nxt[M*2],n,m,k,cnt,num,tot,w[M*2],t,cur=1,fa[M],dep[M],f[M][21],sum,maxn[M][21],ss,s1,s2;
bool bj[M],vis[M];
int read(){
	int f=1,re=0;
	char ch;
	for(ch=getchar();!isdigit(ch)&&ch!='-';ch=getchar());
	if(ch=='-'){
		f=-1;
		ch=getchar();
	}
	for(;isdigit(ch);ch=getchar())
		re=(re<<3)+(re<<1)+ch-'0';
	return re*f;
}
struct qrx{
	int x,y,z,v;
}a[M];
bool comp(qrx a,qrx b){
	return a.z<b.z;
}
int ksm(int a,int b){//快速幂 
	int ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=a*a%mod;
		b>>=1;
	}
	return ans;
}
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void add2(int x,int y,int z){
	nxt[++num]=first[x];
	first[x]=num;
	to[num]=y;
	w[num]=z;
}
void add1(int x,int y,int z){
	a[++tot].x=x;
	a[tot].y=y;
	a[tot].z=z;
}
void kruskal(){//求最小生成树 
	int cnt=0;
	sort(a+1,a+tot+1,comp);
	for(int i=1;i<=tot;i++){
		int u=find(a[i].x);
		int v=find(a[i].y);
		if(u!=v){
			fa[u]=v;
			sum+=a[i].z;
			cnt++;
			vis[i]=1;
			add2(a[i].x,a[i].y,a[i].z);
			add2(a[i].y,a[i].x,a[i].z);
		}
		if(cnt==n-1) break;
	}
}
void init(int u,int faa){ 
	dep[u]=dep[faa]+1;
	for(int i=first[u];i;i=nxt[i]){
		int v=to[i];
		if(v==faa) continue;
		f[v][0]=u;
		maxn[v][0]=w[i];
		init(v,u);
	}
}
void pre(){
    for(int i=1;i<=20;i++)
        for(int j=1;j<=n;j++){
            f[j][i]=f[ f[j][i-1] ][i-1];
            maxn[j][i]=max(maxn[j][i-1],maxn[f[j][i-1]][i-1]);
        }
}
int Lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
	if(x==y) return x;
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
int findmax(int x,int lca)
{
	int ans=0;
    for(int i=20;i>=0;i--)
        if(dep[ f[x][i] ] >= dep[lca]){
            ans=max(ans,maxn[x][i]);
            x=f[x][i];
        }
    return ans;
} 
void work(){//求次小生成树 
	pre();
	for(int i=1;i<=tot;i++){
		if(vis[i]) continue;
		int x=a[i].x,y=a[i].y,z=a[i].z;
		int lca=Lca(x,y);
		a[i].v=z-max(findmax(x,lca),findmax(y,lca));
	}
}
void clear(){//多组数据要清空 
	ss=s1=s2=tot=sum=num=0;
	memset(first,0,sizeof(first));
	memset(nxt,0,sizeof(nxt));
	memset(w,0,sizeof(nxt));
	memset(to,0,sizeof(nxt));
	memset(f,0,sizeof(f));
	memset(vis,0,sizeof(vis));
	memset(dep,0,sizeof(dep));
	memset(a,0,sizeof(a));
	memset(fa,0,sizeof(fa));
	memset(maxn,0,sizeof(maxn));
}
signed main(){
	//freopen("zhuzhu3.in","r",stdin);
	//freopen("zhuzhu.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		clear();
		n=read();m=read();k=read();
		int x,y,z;
		for(int i=1;i<=n;i++) fa[i]=i;
		for(int i=1;i<=m;i++){
			x=read();
			y=read();
			z=read();
			add1(x,y,z);
		}
		kruskal();
		if(sum>k) {//当最小生成树大于k时,无解 
			printf("0\n");
			continue;
		}
		init(1,0);
		work();
		if(sum==k){//sum==k的请况,统计次小生成树的大小也等于k的边数 
			for(int i=1;i<=tot;i++){
				if(vis[i]) continue;
				if(!a[i].v) ss++;
			}
			printf("%lld\n",(ksm(2,m)-(2*ksm(2,m-(n-1)-ss)%mod)+mod)%mod);
			continue;
		}
		if(sum<k){//sum<k的请况,统计次小生成树的大小小于k的边数,次小生成树的大小等于k的边数
			for(int i=1;i<=tot;i++){
				if(vis[i]) continue;
				if(a[i].v+sum<k) s1++;
				if(a[i].v+sum==k) s2++;
			}
			printf("%lld\n",2*((ksm(2,m-(n-1)-s1)-ksm(2,m-(n-1)-s1-s2)+mod)%mod)%mod);
		}
	}
	return 0;
}

T3 shuawang

题目描述

T3

解析

我不会 (考场上写了个暴力,20分)
直接放上题解
在这里插入图片描述

题解

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll read(){
	ll x=0,w=1;
	char ch=0;
	while (ch<'0' || ch>'9'){
		ch=getchar();
		if (ch=='-') w=-1;	
	}
	while (ch<='9' && ch>='0'){
		x=(x<<1)+(x<<3)+ch-'0';
		ch=getchar();
	}
	return x*w;
}
namespace pb_ds{   
    namespace io{
        const int MaxBuff=1<<15;
        const int Output=1<<23;
        char B[MaxBuff],*S=B,*T=B;
		#define getc() ((S==T)&&(T=(S=B)+fread(B,1,MaxBuff,stdin),S==T)?0:*S++)
        char Out[Output],*iter=Out;
        inline void flush(){
            fwrite(Out,1,iter-Out,stdout);
            iter=Out;
        }
    }
    template<class Type> inline void Print(register Type x,register char ch='\n'){
        using namespace io;
        if(!x) *iter++='0';
        else{
            if(x<0) *iter++='-',x=-x;
            static int s[100]; 
            register int t=0;
            while(x) s[++t]=x%10,x/=10;
            while(t) *iter++='0'+s[t--];
        }
        *iter++=ch;
    }
}
using namespace pb_ds;
const int N=5e4+5;
const int M=21;
const int L=17;
const int Mod=1e9+7;
const ll inf=(ll)1e18+1;
int T,n,m,q;
bool p[N][10];
int fail[M],val[L][N][M],trans[M][10],pw[L]={10};
ll dp[N][M],rkl[L][N][M],rkr[L][N][M];
char str[M],s[N],ed[L][N][M];
inline ll fix(ll x){
	return x<inf?x:inf;
}
inline void KMP(){
	fail[0]=fail[1]=0;
	for (int i=2;i<=m;++i){
		int now=fail[i-1];
		while (now && str[now+1]!=str[i]) now=fail[now];
		if (str[now+1]==str[i]) fail[i]=now+1;
		else fail[i]=0;
	}
}
inline void init(){
	for (int i=0;i<m;++i){
		for (int j=0;j<=9;++j){
			int now=i;
			while (now && str[now+1]!=j+'0') now=fail[now];
			if (str[now+1]!=j+'0') trans[i][j]=0;
			else trans[i][j]=now+1;
		}
	}
	for (int j=0;j<=9;++j) trans[m][j]=m;
}
inline void input(){
	n=read(),q=read();
 	scanf("%s%s",str+1,s+1);
 	m=strlen(str+1);
 	for (int i=1;i<=n;++i){
 		if (s[i]=='?') memset(p[i],1,sizeof(p[i]));
 		else memset(p[i],0,sizeof(p[i])),p[i][s[i]-'0']=1;
 	}
 	KMP(),init();
}
inline int query(ll k){
	if (k>dp[1][0]) return -1;
	int x=1,y=0,res=0;
	while (x<=n){
		for (int i=L-1;~i;--i){
			if (x+(1<<i)<=n+1 && rkl[i][x][y]<k && k<=rkr[i][x][y]){
				res=(1ll*res*pw[i]+val[i][x][y])%Mod;
				k-=rkl[i][x][y],y=ed[i][x][y],x+=(1<<i);
			}
		}
		if (x>n) break;
		for (int i=0;i<=9;++i){
			if (k>dp[x+1][trans[y][i]]) k-=dp[x+1][trans[y][i]];
			else{
				res=(10ll*res+i)%Mod;
				++x,y=trans[y][i];
				break;
			}
		}
	}
	return res;
}
int main(){
	freopen("shuawang.in","r",stdin);
    freopen("shuawang.out","w",stdout);
 	for (int i=1;i<L;++i)
 		pw[i]=1ll*pw[i-1]*pw[i-1]%Mod;
 	T=read();
 	while (T--){
 		input();
 		for (int i=0;i<=m;++i) 
 			dp[n+1][i]=(i==m);
 		for (int i=n;i;--i){
 			for (int j=0;j<=m;++j){
 				dp[i][j]=0;
 				ll mx=-1;
 				int nxt=-1;
 				for (int k=0;k<=9;++k){
 					if (p[i][k]){
 						dp[i][j]=fix(dp[i][j]+dp[i+1][trans[j][k]]);
 						if (dp[i+1][trans[j][k]]>mx) nxt=k,mx=dp[i+1][trans[j][k]];
 					}
 				}
 				ed[0][i][j]=trans[j][nxt];
                val[0][i][j]=nxt,rkl[0][i][j]=0;
                for (int k=0;k<nxt;++k)
                	 if (p[i][k]) rkl[0][i][j]=fix(rkl[0][i][j]+dp[i+1][trans[j][k]]);
               	rkr[0][i][j]=fix(rkl[0][i][j]+dp[i+1][trans[j][nxt]]);
 			}
 		}
 		for (int k=1;k<L;++k){
 			int len=(1<<(k-1));
 			for (int i=1;i+(1<<k)<=n+1;++i){
 				for (int j=0;j<=m;++j){
					int x=ed[k-1][i][j];
					ed[k][i][j]=ed[k-1][i+len][x];
					val[k][i][j]=(1ll*val[k-1][i][j]*pw[k-1]+val[k-1][i+len][x])%Mod;
					rkl[k][i][j]=fix(rkl[k-1][i][j]+rkl[k-1][i+len][x]);
					rkr[k][i][j]=fix(rkl[k-1][i][j]+rkr[k-1][i+len][x]);
 				}
 			}
 		}
 		while (q--){
 			ll k=read();
 			Print(query(k));
 		}
 		io::flush();
 	}   
	return 0;
}

暴力代码

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,m,x,cnt,ans[20000009],p[1009],t;
char s1[1009],s2[1009];
const int mod=1e9+7;
void check(){
	bool bj=0;
	for(int i=1,j=0;i<=n;i++){
		while(j>0&&s1[j+1]!=s2[i]) j=p[j];
		if(s1[j+1]==s2[i]) j++;
		if(j==m){
			bj=1;
			break;
		}
	}
	if(bj){
		cnt++;
		for(int i=1;i<=n;i++)
			ans[cnt]=(ans[cnt]<<3)+(ans[cnt]<<1)+s2[i]-'0';
	}
		
}
void dfs(int pos){
	if(pos==n+1){
		check();
		return;
	}
	if(isdigit(s2[pos]))
		dfs(pos+1);
	else
		for(int i=0;i<=9;i++){
			s2[pos]=i+'0';
			dfs(pos+1);
			s2[pos]='?';
		}
}
signed main(){
	freopen("shuawang.in","r",stdin);
	freopen("shuawang.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		cnt=0;
		memset(ans,0,sizeof(ans));
		memset(p,0,sizeof(p));
		scanf("%lld%lld",&n,&q);
		scanf("%s",s1+1);
		m=strlen(s1+1);
		scanf("%s",s2+1);
		for(int i=2,j=0;i<=m;i++){
			while(j>0&&s1[j+1]!=s1[i]) j=p[j];
			if(s1[j+1]==s1[i]) j++;
			p[i]=j;
		}
		dfs(1);
		sort(ans+1,ans+cnt+1);
		for(int i=1;i<=q;i++){
			scanf("%lld",&x);
			if(x<=cnt)
				printf("%lld\n",ans[x]%mod);
			else
				printf("-1\n");
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值