CSP-S 模拟 10
CSP-S 模拟 10
T1 二分图匹配
大力分块。
复杂度 \(O(2 n \sqrt{nm} )\)
设 \(dp[i][j]\) 表示 s1 的第 \(i\) 位匹配 s2 的第 \(j\) 位时,且 i 匹配 j 时,最大匹配数量。
初始化 dp 数组为 \(0\)。
但是空间会爆。
又发现 dp 数组当 \(i\) 增加时,其中所有值一定不会下降,一个简单贪心,很好理解,能多匹配尽量多匹配。
于是我们像背包一样,压掉 \(i\) 这一维。
设当前枚举到 s1 的第 \(k\) 位。
转移方程为
当 \(s2[i]=s1[k]\) 时,
注意倒序枚举 \(i\),防止后效性。
现在复杂度为 \(O(nm^2)\)。
预处理 \(\max\) 可以做到 \(O(2nm)\)。
考虑优化。
发现 dp 数组值最大才到 \(1000\)。
又发现,当转移 \(\forall i\),\(s2[i]=s1[k]\) 且 \(\max_{0\le j < i}dp[j]+1\) 值相同时,只在最小的 \(i\) 处转移即可。
贪心即可证明:显然对于所有上述情况中的 \(i\),\(i\) 越小越好,因为 \(i\) 越小,留给 s1 后面的字符的选择空间越大,最终答案就会大。而其他的 \(i\) 就白白浪费了 s2 中 \(i(\min)\) 与 \(i\) 中间的字符,答案一定不优。
显然不同的转移最多只有 \(n\) 次。
我们先开 vector 存每个字符在 s2 中出现位置。
我们需要一个可以支持单点修改,区间查询最大值的东西。
然后我们有两种办法实现转移:
二分套线段树 | 线段树上二分
直接 vector 里二分出第一个 max 改变的位置,转移,不断重复这个过程。
复杂度 \(O(n^2 \log^2 n)\)。
代码没写。
分块(下面代码复杂度不正确,改为跳块才正确)
我们对 s2 分块,记录每个区间最大值为 ma1,枚举 s1 的每一位时计算 ma1 的前缀 \(\max\),记进 ma2。
设之前由 \(last\) 这个前缀 \(\max\) 转移而来。
从前往后枚举每个块,若这个块内存在 i 使 \(s2[i]=s1[k]\),并且包含这个块的前缀 \(\max\) 与 \(last\) 不等,则暴力遍历这个块,尝试转移。
简单处理一下即可做到无后效性。
复杂度看似不对。
首先我们需要枚举 s1 的每一位,\(O(n)\)。
对于每一位,我们有:
预处理前缀 \(\max\)(ma2)的复杂度 \(O(\frac{m}{B})\)。
暴力遍历 \(O(2B)\)。(尝试转移时若无法转移,则下一次尝试时一定可以转移)。
遍历每一块 \(O(\frac{m}{B})\)。
总复杂度 \(O(2B+\frac{2m}{B})\)。
基本不等式可得复杂度 \(O(2 \sqrt{nm})\)。
代码见下面赛时杂糅代码,枚举块改成了在 vector 里二分。
赛时杂糅代码(既有二分,又有分块)
#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=1<<20;
int n,m;
char s1[N],s2[N];
vector<int> v[200];
int maxn[N];
int id[N];
const int len=33;
int ma1[N];
int ma2[N];
int solve(char c,int l,int r,int id)
{
int p=v[c][l];
int ma=ma2[id-1];
for(int i=(id-1)*len;i<p;i++) ma=max(ma,maxn[i]);
int nw=maxn[p];
maxn[p]=ma+1;
ma1[id]=max(ma1[id],maxn[p]);
// as=ma;
int last=ma;
ma=max(ma,nw);
for(int i=l+1;i<=r;i++)
{
p=v[c][i];
for(int j=v[c][i-1]+1;j<p;j++) ma=max(ma,maxn[j]);
nw=maxn[p];
if(ma!=last)
{
maxn[p]=ma+1;
ma1[id]=max(ma1[id],maxn[p]);
last=ma;
// as=ma;
}
ma=max(ma,nw);
}
return last;
}
// void output()
// {
// for(int i=1;i<=m;i++) cout<<maxn[i]<<" ";
// // for(int i=1;i<=m;i++) cout<<"i="<<i<<" maxn="<<maxn[i]<<"\n";
// cout<<"\n";
// }
signed main()
{
// #ifndef ONLINE_JUDGE
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
// #endif
cin>>n>>m;
scanf("%s",s1+1);
scanf("%s",s2+1);
for(int i=1;i<=m;i++)
{
id[i]=i/len+1;
v[s2[i]].push_back(i);
}
// cout<<s2[98]<<" "<<s2[99]<<" "<<s1[975]<<"\n";//B
for(int i=1;i<=n;i++)
{
// cerr<<"i="<<i<<" ";
for(int j=1;j<=id[m];j++)
ma2[j]=max(ma2[j-1],ma1[j]);
ma2[id[m]+1]=ma2[id[m]];
int ma=-1;
for(int j=0;j<v[s1[i]].size();j++)
{
// cerr<<"j="<<j<<" ";
int p=v[s1[i]][j];
int nw=ma2[id[p]];
if(nw==ma)
{
// cerr<<"uhfe;oa";
for(int k=20;k>=0;k--)
{
int to=j+(1<<k);
if(to<v[s1[i]].size())
{
int p_to=v[s1[i]][to];
if(ma2[id[p_to]]==ma) j=to;
}
}
// cerr<<j<<" ";
continue;
}
int l=j,r=j;
while(j<v[s1[i]].size()-1&&id[v[s1[i]][j]]==id[v[s1[i]][j+1]]) j++;
r=j;
int as=solve(s1[i],l,r,id[p]);
if(as!=ma) ma=as;
// int pre=ma2[id[p]-1];
}
// for(int j=v[s1[i]].size()-1;j>=0;j--)
// {
// int p=v[s1[i]][j];
// if(ma2[id[p-1]])
// }
// if(maxn[98]==84&&maxn[101]==84) cout<<i<<"\n";
// if(i!=974&&i!=975) continue;
// cout<<"i="<<i<<" ma="<<ma<<"\n";
// output();
// cout<<"\n";
}
int ans=0;
for(int i=1;i<=m;i++) ans=max(ans,maxn[i]);
cout<<ans<<"\n";
return 0;
}
T2 虚图
最短路简单优化。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int Size=(1<<20)+1;
char buf[Size],*p1=buf,*p2=buf;
char buffer[Size];
int op1=-1;
const int op2=Size-1;
#define getchar() \
(tt == ss && (tt=(ss=In)+fread(In, 1, 1 << 20, stdin), ss == tt) \
? EOF \
: *ss++)
char In[1<<20],*ss=In,*tt=In;
inline int read()
{
int x=0,c=getchar(),f=0;
for(;c>'9'||c<'0';f=c=='-',c=getchar());
for(;c>='0'&&c<='9';c=getchar())
x=(x<<1)+(x<<3)+(c^48);
return f?-x:x;
}
inline void write(int x)
{
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int N=2e5+5;
int n,m,t;
vector<int> E[N],V[N];
void add(int u,int v,int w)
{
E[u].push_back(v);
V[u].push_back(w);
}
bool vis[N],vis2[N];
int dis[N];
const int MAXN=0x3f3f3f3f3f3f3f3f;
priority_queue<pair<int,int> ,vector<pair<int,int> > ,greater<pair<int,int> > > q;
queue<int> q2;
int dij(int s,int max_len)
{
while(q2.size())
{
int nw=q2.front();
q2.pop();
vis[nw]=0;
dis[nw]=MAXN;
}
while(q.size()) q.pop();
// for(int i=1;i<=n;i++) vis[i]=0,dis[i]=MAXN;
q.push(make_pair(0,s));
dis[s]=0;
q2.push(s);
while(q.size())
{
int x=q.top().second;
q.pop();
if(vis[x]) continue;
vis[x]=1;
if(vis2[x]&&x!=s) return dis[x];
for(int i=0;i<E[x].size();i++)
{
int to=E[x][i];
int w=V[x][i];
if(vis[to]) continue;
if(dis[x]+w>=max_len) continue;
if(dis[to]>dis[x]+w)
{
dis[to]=dis[x]+w;
q.push(make_pair(dis[to],to));
q2.push(to);
}
}
}
return max_len;
}
signed main()
{
// #ifndef ONLINE_JUDGE
freopen("map.in","r",stdin);
freopen("map.out","w",stdout);
// #endif
memset(dis,0x3f,sizeof(dis));
n=read();
m=read();
t=read();
for(int i=1;i<=m;i++)
{
int u=read(),v=read(),w=read();
add(u,v,w);
add(v,u,w);
}
while(t--){
int x=read();
vis2[x]=1;
}
int ans=MAXN;
for(int i=1;i<=n;i++)
if(vis2[i]) ans=min(ans,dij(i,ans));
cout<<ans;
return 0;
}
T3 冒泡
T4 亲戚
以下是博客签名,正文无关
本文来自博客园,作者:Wy_x,转载请在文首注明原文链接:https://www.cnblogs.com/Wy-x/articles/19036994
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC-BY-NC-SA 4.0 协议)进行许可。

浙公网安备 33010602011771号