妈蛋这道普及组水(神)题搞了我很久。
一、
首先一个非常显然的事情就是每个火车告诉了站与站之间的等级关系,所以拓扑求最长路。
但是发现暴力建边的话最坏可以达到500*500,所以时间复杂度有 O(MN2)≈2.5∗108 ,常数相当小。。数据水成狗,所以绝对可以过的。
二、
所以我就想到了bitset,把每辆火车做成一个长N的布尔向量,经过为1,不经过为0,第一个车站的左边和最后一个车站的右边补1,。然后对于每个车站,把所有它所在的位为1的向量都&起来,然后扫一遍向量连边。
这样做的时间复杂度可以用long long模拟bitset的时间复杂度来估计,就是 O(MN264)≈107 ,常数更小了,实际跑起来其实跟10^6差不多。
三、
然后我看了一个大神的代码,发现原来是有正儿八经的O(NM)的做法的。
我们发现车站之间是比较麻烦的,所以考虑对偶转换!!
我们这样来考虑,比如说我们设火车经过的最低等级的车站为火车的等级,那么火车的等级数=车站的等级数?
按照上面的定义,火车的等级数自然是小于等于车站的等级数的,而如果一个车站不是任何一辆火车的等级,那么就意味着它可以下降或上升它自己的等级直到它是任何一辆火车的等级呢?
但是这样的前提是每一辆车站都至少有一辆火车经过,所以自然我们只要加一辆经过所有车站的火车就可以了。
这样的话,我们只需要求出火车的等级即可了!
但是火车的等级怎么求呢?如果不存在一个车站使得两辆火车都经过它,那么显然这两辆火车之间是没有直接的等级关系的;而如果它们有交的话,那么显然在交集部分经过更多车站的火车的等级应该是更低的,因为低等级的车站会经过所有高等级的车站经过的车站!
一定要注意的地方:
①拓扑求最长路的时候是要求Max!!
②一定要对拍!
code(bitset):
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
inline void in(int &x){
char c=getchar();
x=0;
while(c<'0'||c>'9')c=getchar();
for(;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
int l[1005],r[1005];
#include<bitset>
bitset<1005> be[1005],btmp;
int next[1000005],ptr[1005],succ[1000005];
int stack[1005],level[1005],ru[1005];
int main(){
freopen("level2013.in","r",stdin);
freopen("level_TA.out","w",stdout);
int n,m,i,j,stop,s;
in(n),in(m);
for(i=m;i--;){
in(s);
in(l[i]);
for(--s;--s;){
in(stop);
be[i][stop]=1;
}
in(r[i]);
for(j=l[i];j;--j)be[i][j]=1;
for(j=r[i];j<=n;++j)be[i][j]=1;
}
int etot=1;
for(i=n;i;--i){
btmp.set();
for(j=m;j--;)
if(l[j]<=i&&i<=r[j]&&be[j][i]){
//cout<<"Get:"<<j<<endl;
btmp&=be[j];
}
for(j=n;j;--j)
if(~btmp[j]){
next[etot]=ptr[i],ptr[i]=etot,succ[etot++]=j;
//cout<<i<<"->"<<j<<":"<<ptr[i]<<"->"<<next[etot-1]<<endl;
++ru[j];
}
}
int top=0;
for(i=n;i;--i)
if(ru[i]==0){
stack[top++]=i;
level[i]=1;
//cout<<"First into stack:"<<i<<endl;
}
int nowlevel;
while(top--){
nowlevel=level[stack[top]];
//cout<<"----"<<stack[top]<<"-----\n";
for(i=ptr[stack[top]];i;i=next[i]){
level[succ[i]]=max(level[succ[i]],nowlevel+1);
//cout<<i<<":"<<nowlevel+1<<"->"<<succ[i]<<endl;
if(--ru[succ[i]]==0)stack[top++]=succ[i];
}
}
printf("%d\n",*max_element(level+1,level+n+1));
}
code(对偶转换):
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
inline void in(int &x){
char c=getchar();
while(c<'0'||c>'9')c=getchar();
x=0;
for(;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
int num[1005][1005],stop[1005][1005];
int stack[1005];
int next[1000005],ptr[1005],succ[1000005],ru[1005],etot=1;
int level[1005];
inline void addedge(int u,int v){
next[etot]=ptr[u],ptr[u]=etot,++ru[v],succ[etot++]=v;
}
int main(){
freopen("level2013.in","r",stdin);
freopen("level2013.out","w",stdout);
int n,m,i,j,k,tmp;
in(n),in(m);
++m;
for(i=n;i;--i)num[0][i]=stop[0][i]=i;
stop[0][0]=n;
for(i=m;--i;){
in(stop[i][0]);
for(j=1,k=1;j<=stop[i][0];++j)
for(in(stop[i][j]);k<stop[i][j];++k)
num[i][k]=j-1;
--j;
while(k<=n)num[i][k++]=j;
}
int l,r;
for(i=m;i--;)
for(j=i;j--;){
l=max(stop[i][1],stop[j][1])-1;
r=min(stop[i][stop[i][0]],stop[j][stop[j][0]]);
if(l<=r)
if(num[i][r]-num[i][l]>num[j][r]-num[j][l])addedge(i,j);
else if(num[i][r]-num[i][l]<num[j][r]-num[j][l])addedge(j,i);
}
int top=0;
for(i=m;i--;)
if(!ru[i]){
stack[top++]=i;
level[i]=1;
}
int nowlevel;
while(top--){
nowlevel=level[stack[top]]+1;
for(i=ptr[stack[top]];i;i=next[i]){
level[succ[i]]=max(level[succ[i]],nowlevel);
if(!--ru[succ[i]])stack[top++]=succ[i];
}
}
printf("%d\n",*max_element(level,level+m));
}