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
10pts:n4暴力
40
p
t
s
:
n
3
40pts:n^3
40pts:n3暴力 只不过存了下出现的数
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
al−ak=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 T4:Vani和 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;
}