2021.7.12 提高B组模拟赛

2021.7.12 2021.7.12 2021.7.12 模拟赛 Ⅰ Ⅰ
目录:

T1.好元素
T2.最短路径
T3.最长公共子串
T4.Vani和Cl2捉迷藏

T 1 : T1: T1好元素

在这里插入图片描述

分析:

10 p t s : n 4 10pts:n^4 10ptsn4暴力
40 p t s : n 3 40pts:n^3 40ptsn3暴力 只不过存了下出现的数
70 p t s : 70pts: 70pts预处理两个数的和 这样就 n 2 n^2 n2
100 p t s : 100pts: 100pts哈希
移项可得 a l − a k = a i + a j a_l-a_k=a_i+a_j alak=ai+aj
把右边丢进哈希 去匹配左边就行了 最后卡了卡常就过辽

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#pragma GCC optimize(2)
#define reg register
using namespace std;
typedef long long ll;
const int Mod=25000003,N=1e4+5,X=689207157;
int n,a[N],hash[Mod],qwq,ans;
int val(int x){return (x%Mod+Mod)%Mod;}
int locate(int x)
{
	int q=val(x),i=0;
	while(i<Mod&&hash[(i+q)%Mod]!=X&&hash[(i+q)%Mod]!=x) i++;
	return (i+q)%Mod;
}
void ins(int x)
{
	int q=locate(x);
	hash[q]=x;
	return;
}
bool Hash(int x)
{
	int q=locate(x);
	return hash[q]==x;
}
int main()
{
//	freopen("good.in","r",stdin);
//	freopen("good.out","w",stdout);
	for(reg int i=0;i<Mod;i++)
		hash[i]=X;
	scanf("%d",&n);
	for(reg int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		qwq=0;
		for(reg int j=1;j<i;j++)
		{
			if(Hash(a[i]-a[j])&&!qwq)
			{
				ans++;
				qwq=1;
				break;
			}
		}
		for(reg int k=1;k<=i;k++) 
			ins(a[i]+a[k]);
	}
	printf("%d",ans);
	
	return 0;
}

T 2 : T2: T2最短路径

在这里插入图片描述
在这里插入图片描述

分析:

d p dp dp
f i , j f_{i,j} fi,j表示来时走到 i i i 回去时走到 j j j的最短路径
k k k来更新 那么 k = m a x ( i , j ) + 1 k=max(i,j)+1 k=max(i,j)+1
f i , k = m i n { f i , k , f i , j + d i s ( j , k ) } f_{i,k}=min\{f_{i,k},f_{i,j}+dis(j,k)\} fi,k=min{fi,k,fi,j+dis(j,k)}
f k , j = m i n { f j , k , f i , j + d i s ( i , k ) } f_{k,j}=min\{f_{j,k},f_{i,j}+dis(i,k)\} fk,j=min{fj,k,fi,j+dis(i,k)}
距离就套 E u c l i d Euclid Euclid公式

对于 m a x ( i , j ) = n max(i,j)=n max(i,j)=n时特判
i = j i=j i=j f n , n = m i n { f n , n , f n , j + d i s ( j , n ) } f_{n,n}=min\{f_{n,n},f_{n,j}+dis(j,n)\} fn,n=min{fn,n,fn,j+dis(j,n)}
j = n j=n j=n f n , n = m i n { f n , n , f i , n + d i s ( i , n ) } f_{n,n}=min\{f_{n,n},f_{i,n}+dis(i,n)\} fn,n=min{fn,n,fi,n+dis(i,n)}
再判一下过去不走 b 1 b1 b1 回来不走 b 2 b2 b2就行了

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N=1005;
double f[N][N],x[N],y[N];
int n,b1,b2;
double dist(int a,int b)
{
	double X=(double)(x[a]-x[b])*(x[a]-x[b]);
	double Y=(double)(y[a]-y[b])*(y[a]-y[b]);
	return (double)sqrt(X+Y);
}
int main(){
//	freopen("path.in","r",stdin);
//	freopen("path.out","w",stdout);
	scanf("%d%d%d",&n,&b1,&b2);
	b1++;b2++;
	for(int i=1;i<=n;i++)
		scanf("%lf%lf",&x[i],&y[i]);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) 
			f[i][j]=double(1e6);
	f[1][1]=0.0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(i!=j||i==1)
			{
				int k=max(i,j)+1;
				if(k==n+1)
				{
					if(i==n) f[n][n]=min(f[n][n],f[n][j]+dist(j,n));
					if(j==n) f[n][n]=min(f[n][n],f[i][n]+dist(i,n));
				}else
				{
					if(k!=b1) f[i][k]=min(f[i][k],f[i][j]+dist(j,k));
					if(k!=b2) f[k][j]=min(f[k][j],f[i][j]+dist(i,k));
				}
			}	
	printf("%.2lf",f[n][n]);
	return 0;
}

T 3 : T3: T3最长公共子串

在这里插入图片描述
在这里插入图片描述

分析:

考虑每个字符都是长度为 1 1 1的区间
枚举两个字符串开始的地方 往左右匹配 左右有一个就可以继续
如果可以整段匹配 就匹配下一个 不然就要结束

这样还是不行 就要预处理 看从某段 和字符串某个位置出发 到左右最长匹配有多长 ( ( (匹配这一段 ) ) )
然后就去记搜 看是不是整段匹配 ( ( (长度相同 ) ) ) 就可以接着搜了

CODE:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=2005; 
struct node{
	int l,r;
}a[N*50],k[N];
int lent,lens,m,l[N][N],r[N][N],n,tot=1,f[N][N],ans,num[N][28];
int dl[N],dr[N];
char t[N],s[N];
bool cmp(node x,node y){return x.l<y.l;}
int query(int x,int y)
{
	if(x>n||y>lent) return 0;
	if(f[x][y]) return f[x][y];
	if(r[x][y]!=k[x].r-k[x].l+1) return f[x][y]=r[x][y];  //长度不相同
	else return f[x][y]=r[x][y]+query(x+1,y+r[x][y]);  //相同就继续匹配
}
int main(){
//	freopen("lcs.in","r",stdin);
//	freopen("lcs.out","w",stdout); 
	scanf("%s",t+1);
	lent=strlen(t+1);
	scanf("%s",s+1);
	lens=strlen(s+1);
	scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		a[i].l++;a[i].r++;
	}
	sort(a+1,a+m+1,cmp);
	dl[1]=a[1].l;dr[1]=a[1].r;
	for(int i=2;i<=m;i++)
	{
		if(a[i].l<=dr[tot]) dr[tot]=max(dr[tot],a[i].r);
		else dl[++tot]=a[i].l,dr[tot]=a[i].r;
	}
	if(dl[1]>1)
		for(int i=1;i<=dl[1]-1;i++)
			k[++n]=(node){i,i};
	dl[tot+1]=lens+1;
	for(int i=1;i<=tot;i++)
	{
		k[++n]=(node){dl[i],dr[i]};
		for(int j=dr[i]+1;j<=dl[i+1]-1;j++)
			k[++n]=(node){j,j};  //处理每一小段
	}	
	for(int i=1;i<=n;i++)
		for(int j=k[i].l;j<=k[i].r;j++)
			num[i][s[j]-'a']++;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=lent;j++)
		{
			int tmp=0;
			while(tmp+j<=lent&&num[i][t[j+tmp]-'a'])
				num[i][t[j+tmp]-'a']--,tmp++;
			r[i][j]=tmp;
			for(int u=0;u<tmp;u++)
				num[i][t[j+u]-'a']++;  //往右匹配最长
			tmp=0;
			while(j-tmp>=1&&num[i][t[j-tmp]-'a'])
				num[i][t[j-tmp]-'a']--,tmp++;
			l[i][j]=tmp;
			for(int u=0;u<tmp;u++)
				num[i][t[j-u]-'a']++;  //往左匹配最长
		}
	for(int i=1;i<=n+1;i++)
		for(int j=1;j<=lent+1;j++)
			ans=max(ans,l[i-1][j-1]+query(i,j));  //左区间也做(上面的只有右区间)
	printf("%d",ans);
	
	return 0;
}

T 4 : V a n i T4:Vani T4Vani C l 2 Cl2 Cl2捉迷藏

在这里插入图片描述

分析:

在这里插入图片描述
因为这句话 可以理解为求可相交最小路径覆盖
因为是可相交 要先做传递闭包 ( ( (就是 f l o y d floyd floyd ) ) )
最后最小路径覆盖 = = = 结点数 − - 最大匹配

所以跑完 f l o y d floyd floyd 再跑个模板最大匹配就行了

CODE:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=205;
int n,m,G[N][N],ans,vis[N],link[N];
int found(int x)  //最大匹配
{
	for(int i=1;i<=n;i++)
		if(!vis[i]&&G[x][i])
		{
			vis[i]=1;
			if(!link[i]||found(link[i]))
			{
				link[i]=x;return 1;
			}
		}
	return 0;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		G[x][y]=1;
	}
	for(int k=1;k<=n;k++)
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				if(G[i][k]&&G[k][j])
					G[i][j]=1;  //传递闭包
	for(int i=1;i<=n;i++)
	{
		memset(vis,0,sizeof(vis));
		ans+=found(i);
	}
	printf("%d",n-ans);
	
	return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值