
HMF-have much fun Site
文章
标签
16

阅

【题解】CF818G Four Melodies
热度: loading...
审核求过
这是在咕了一周的每日总结以及一次每周总结的情况下写的题解
其实蛮简单的....就是没有有图的题解。
考虑每个只能选一次的限制,这个太典了,直接拆点,连一条容量 ,费用 的边,走了这条边意味着选了 , 并且产生 的贡献。
然后思考两个限制,相邻元素绝对值差为 ,或模 同余。
显然有这么一种思路,从 向之后所有的与它差为 或模 同余的点的 连边,但这样边数是 的,显然过不了。
考虑从 到 ,如果中间有一个一样可以到达 , 的点 , 那么从 到 ,再从 到 是完全可以替代 的。
因此继续拆点,将再拆成 ,用于与绝对值差为 ,以及膜 同余的 最近点 连边。
具体连边像这样(容量、费用)

然后在相邻的模 同余的点的 之间连 的边(不选),在相邻的 相同的点的 之间连 的边(这些是表示不用选自己就可以选到自己后面的)。从每个点的 向之后最近的 的点和 的点的 连 的边,向之后最近的模 同余的节点的 连 的边。(表示选了自己后能选后面的)
然后从源点向每个点的 连 的边,从每个点的 向汇点连 。
最后有一个子序列数量的限制,我们建一个超级源点,向源点连容量为需要的子序列数,费用为 的边。(所以其实 都是能做的)
附上样例二的建图

很丑陋吧?画图画的
附上代码
// Problem: CF818G Four Melodies
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF818G
// Memory Limit: 1000 MB
// Time Limit: 5000 ms
#include<bits/stdc++.h>
#define N 20010
#define M 1000010
using namespace std;
namespace MCMF{
int cnt=1,head[N],to[M],nxt[M],val[M],flow[M];
const int INF=0x3f3f3f3f;
void insert(int u,int v,int f,int w){
cnt++;
to[cnt]=v;
val[cnt]=w;
flow[cnt]=f;
nxt[cnt]=head[u];
head[u]=cnt;
}
void ins(int u,int v,int f,int w) {
insert(u,v,f,w);
insert(v,u,0,-w);
}
int wat[N],dis[N],vis[N],fr[N];
int SPFA(int ss,int tt) {
memset(dis,-1,sizeof(dis));
queue<int> q;
q.push(ss),dis[ss]=0,wat[ss]=INF,vis[ss]=1;
while(!q.empty()) {
int now=q.front();q.pop();
vis[now]=0;
for(int i=head[now]; i; i=nxt[i])
if(flow[i]&&dis[to[i]]<dis[now]+val[i]) {
dis[to[i]]=dis[now]+val[i];
fr[to[i]]=i;
wat[to[i]]=min(wat[now],flow[i]);
if(!vis[to[i]]) q.push(to[i]),vis[to[i]]=1;
}
}
return dis[tt]!=-1;
}
pair<int,int> Dinic(int ss,int tt) {
int rf=0,rv=0;
while(SPFA(ss,tt)) {
rf+=wat[tt],rv+=wat[tt]*dis[tt];
int now=tt;
while(now!=ss) {
flow[fr[now]]-=wat[tt];
flow[fr[now]^1]+=wat[tt];
now=to[fr[now]^1];
}
}
return make_pair(rf,rv);
}
}
using namespace MCMF;
int read() {
int res=0,f=1;char ch=getchar();
while(!isdigit(ch)) f=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch)) res=res*10+ch-'0',ch=getchar();
return f*res;
}
int S,s,T,n,num[N];
int main()
{
n=read();s=4*n+1,T=4*n+2;
for(int i=1; i<=n; i++) num[i]=read();
ins(S,s,4,0);
for(int i=1; i<=n; i++) {
ins(s,2*n+i,INF,0),ins(i,2*n+i,INF,0),ins(n+i,2*n+i,INF,0);
ins(2*n+i,3*n+i,1,1),ins(3*n+i,T,INF,0);
for(int j=i+1;j<=n;j++)if(num[i]-num[j]==1){ins(n*3+i,n+j,INF,0);break;}
for(int j=i+1;j<=n;j++)if(num[j]-num[i]==1){ins(n*3+i,n+j,INF,0);break;}
for(int j=i+1;j<=n;j++)if(num[i]%7==num[j]%7){ins(n*3+i,j,INF,0),ins(i,j,INF,0);break;}
for(int j=i+1;j<=n;j++)if(num[i]==num[j]){ins(n+i,n+j,INF,0);break;}
}
cout<<Dinic(S,T).second;
return 0;
}
赏
