题意
给定一个长度为n的序列,你需要选出4个没有交集的子序列,使得它们都满足:相邻两项要么差的绝对值为1,要么模7余数相同
求最长的总长度
4≤n≤3000
分析
首先我们想想怎么建图,可以把四个子序列变为流量,一个点变成费用,就跑最大费用最大流,每个点只能流一次,我们想到拆两个点一个入一个出
入的连到出的流量为1,费用为1,代表用这个点,
出的连到所有到达入的点,流量为inf,费用为0
然后出点连到终点,起点连到入点
这样有N^2的建边,我们考虑优化
方法1:当你找不到任何优化的点的时候,可以考虑搞一个出错率极小的做法,有一个想法就是每个点仅向后面50个点连边,这样我判断一波好像极其难卡,可是好像我的费用流会T,我看有人这样就过了
方法2:正解,其实我们连模7相同的边和+1,-1边有一个小优化,就是只连到最近的一条,然后再从入点连向出点一条流量为inf,费用为0的边,正确性显然
这样边数就变成O(N)的了
代码
#include <bits/stdc++.h>
#define MP make_pair
using namespace std;
const int N = 50010;
const int inf = 1e9;
typedef pair<int,int> pii;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
struct node{int x,y,d,c,next;}edge[N<<4]; int len,first[N];
void ins(int x,int y,int c,int d)
{
len++; edge[len].x=x; edge[len].y=y; edge[len].c=c; edge[len].d=d; edge[len].next=first[x]; first[x]=len;
len++; edge[len].x=y; edge[len].y=x; edge[len].c=0; edge[len].d=-d; edge[len].next=first[y]; first[y]=len;
}
bool v[N]; int d[N]; queue<int>q; int pre[N],pos[N]; int s,t;
bool bfs()
{
while(!q.empty()) q.pop(); q.push(s);
memset(d,-63,sizeof(d)); d[s]=0;
memset(v,0,sizeof(v)); v[s]=1;
while(!q.empty())
{
int x=q.front();
for(int k=first[x];k!=-1;k=edge[k].next)
{
int y=edge[k].y;
if(d[y] < d[x] + edge[k].d && edge[k].c)
{
d[y] = d[x] + edge[k].d; pos[y] = x; pre[y]=k;
if(!v[y]){v[y]=1; q.push(y);}
}
}
q.pop(); v[x] = 0;
}
return d[t] >= 0;
}
int n,a[N];
int main()
{
len=1; memset(first,-1,sizeof(first));
n = read(); for(int i=1;i<=n;i++) a[i] = read();
int ss=2*n+1; s = ss+1; t = s+1; ins(s,ss,4,0);
for(int i=1;i<=n;i++)
{
ins(ss,i+n,1,0);
ins(i+n,i,1,1);
ins(i,t,1,0);
}
for(int i=1;i<=n;i++)
{
int cnt = 0;
#define MAGIC 1
for(int j=i+1;j<=n;j++)
if((a[i] - a[j] == 1) && cnt < MAGIC){ins(i,j+n,inf,0); cnt++;}
cnt=0;
for(int j=i+1;j<=n;j++)
if((a[i] - a[j] == -1) && cnt < MAGIC){ins(i,j+n,inf,0); cnt++;}
cnt=0;
for(int j=i+1;j<=n;j++)
if((a[i]%7 == a[j]%7) && cnt < MAGIC){ins(i,j+n,inf,0); cnt++;}
ins(i+n,i,inf,0);
}
int ans = 0;
while(bfs())
{
for(int i=t;i!=s;i=pos[i]) ans+=edge[pre[i]].d,edge[pre[i]].c--,edge[pre[i]^1].c++;
}
return printf("%d\n",ans),0;
}