2020杭电暑期多校第二场
1012.String-Distance
标签:dp求LCS
因为插入操作可以被删除操作替代,所以问题也就转化成求A、B两串的最长公共子序列(LCS)
先预处理出g[i][c]表示[ai,ai+1,…,an]中第一个字符c的下标
dp[i][j]表示用B串前i位匹配到j个字符时 在A串中最小的位置
然后就可以O(m^2)转移了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5,INF=0x3f3f3f3f;
int T,q,l,r;
char s[N],t[25];
vector<int>G[30];
int dp[25][25];
int g[N][30];
int ans[N];
struct query{
int l,r,id;
}Q[N];
int main()
{
cin>>T;
while(T--)
{
scanf("%s",s+1);
scanf("%s",t+1);
int ls=strlen(s+1),lt=strlen(t+1);
for (int j=0;j<=25;j++) g[ls+1][j]=INF;
for (int i=ls;i>=1;i--)
{
for (int j=0;j<=25;j++)
if ((s[i]-'a')==j) g[i][j]=i;
else
{
g[i][j]=g[i+1][j];
}
}
cin>>q;
while(q--)
{
scanf("%d%d",&l,&r);
memset(dp,INF,sizeof(dp));
int mx=0;
for (int i=0;i<=lt;i++) dp[i][0]=l-1;
for (int i=1;i<=lt;i++)
{
if (g[dp[i-1][0]+1][t[i]-'a']<=r)
dp[i][1]=min(dp[i-1][1],g[dp[i-1][0]+1][t[i]-'a']);
else
dp[i][1]=dp[i-1][1];
if (dp[i][1]!=INF) mx=max(mx,1);
}
for (int i=2;i<=lt;i++)
for (int j=2;j<=i;j++)
{
if (dp[i-1][j-1]!=INF && g[dp[i-1][j-1]+1][t[i]-'a']<=r)
dp[i][j]=min(dp[i-1][j],g[dp[i-1][j-1]+1][t[i]-'a']);
else
dp[i][j]=dp[i-1][j];
if (dp[i][j]!=INF) mx=max(mx,j);
}
printf("%d\n",r-l+1+lt-2*mx);
}
}
return 0;
}
1005.New-Equipments
把问题转换成n个二次函数图像,比较重要的是观察到每个工人只需要考虑对称轴附近的50个数,那么最多只需要考虑2500个数就可以了。
把所有工人作为一个点和源点连边,把需要考虑的数与汇点连边,再把每个工人与其需要考虑的数连边,距离为花费。
然后开始跑最小费用最大流,每找到一条增广路就记录一次答案。
#include<bits/stdc++.h>
#define debug(x) cerr<<#x<<" : "<<x<<endl;
using namespace std;
typedef long long ll;
const int MAXN=11000;
int T,n,m,top;
ll ans;
bool vis[MAXN];
int s,t,pre[MAXN],last[MAXN],flow[MAXN],maxflow;
ll dis[MAXN],mincost;
struct Edge{
int to,next,flow;//flow流量 dis花费
ll dis;
}edge[MAXN];
int head[MAXN],num_edge;
queue <int> q_SPFA;
void add_edge(int from,int to,int flow,ll dis)
{
edge[++num_edge].next=head[from];
edge[num_edge].to=to;
edge[num_edge].flow=flow;
edge[num_edge].dis=dis;
head[from]=num_edge;
}
bool spfa(int s,int t)
{
memset(dis,0x7f,sizeof(dis));
memset(flow,0x7f,sizeof(flow));
memset(vis,0,sizeof(vis));
q_SPFA.push(s); vis[s]=1; dis[s]=0; pre[t]=-1;
while (!q_SPFA.empty())
{
int now=q_SPFA.front();
q_SPFA.pop();
vis[now]=0;
for (int i=head[now]; i!=-1; i=edge[i].next)
{
if (edge[i].flow>0 && dis[edge[i].to]>dis[now]+edge[i].dis)//正边
{
dis[edge[i].to]=dis[now]+edge[i].dis;
pre[edge[i].to]=now;
last[edge[i].to]=i;
flow[edge[i].to]=min(flow[now],edge[i].flow);
if (!vis[edge[i].to])
{
vis[edge[i].to]=1;
q_SPFA.push(edge[i].to);
}
}
}
}
return pre[t]!=-1;
}
void MCMF()
{
while (spfa(s,t))
{
int now=t;
maxflow+=flow[t];
mincost+=1ll*flow[t]*dis[t];
top++;
printf("%lld%c",mincost,top==n?'\n':' ');
if (top==n) return;
while (now!=s)
{
edge[last[now]].flow-=flow[t];
edge[last[now]^1].flow+=flow[t];
now=pre[now];
}
}
}
ll a[55],b[55],c[55];
set<int>S;
vector<int>G[55];
map<int,int>ord;
void init()
{
top=0;
num_edge=-1;
memset(head,-1,sizeof(head));
memset(edge,0,sizeof(edge));
S.clear();
for (int i=1;i<=n;i++) G[i].clear();
ord.clear();
maxflow=mincost=0;
}
void sol()
{
cin>>n>>m;
init();
for (int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&a[i],&b[i],&c[i]);
int mid=b[i]/(-2*a[i]);
int l=mid-25,r=mid+25;
if (r<1)
{
l=1,r=min(m,50);
}
else
{
if (l>m)
{
r=m;l=max(1,r-49);
}
else
{
if (l<1)
{
r+=1-l;
l=1;
}
if (r>m)
{
l-=r-m;
r=m;
}
if (l<1) l=1;
}
}
for (int j=l;j<=r;j++)
{
S.insert(j);
G[i].push_back(j);
}
}
s=0,t=n+S.size()+1;
for (int i=1;i<=n;i++)
{
add_edge(s,i,1,0);
add_edge(i,s,0,0);
}
int tmp=0;
for (auto x:S)
{
ord[x]=++tmp;
add_edge(n+ord[x],t,1,0);
add_edge(t,n+ord[x],0,0);
}
for (int i=1;i<=n;i++)
{
for (auto to:G[i])
{
add_edge(i,n+ord[to],1,(a[i]*to*to+b[i]*to+c[i]));
add_edge(n+ord[to],i,0,-1ll*(a[i]*to*to+b[i]*to+c[i]));
}
}
MCMF();
}
int main()
{
cin>>T;
while(T--)
sol();
return 0;
}