众所周知,根据“M 理论”对宇宙的描述,无数的平行宇宙漂浮在广阔无垠的物质世界里,而我们的世界只是众多宇宙泡泡中不起眼的一个。
在公元XXXX 年,伟大的Q 博士终于创造了一种能在宇宙和宇宙中穿梭的通道,从此在科学一个黑暗的领域打开了光明的大门。由于技术还很不成熟,所以刚开始一个宇宙只能打通一条到另外一个宇宙的通道,通道是单向的,经过每条通道所花费的时间是单位1,而我们所在的宇宙被称为1号宇宙或root宇宙。
不久人们决定新建一些单向通道来满足日益膨胀的交通需求,同时有一个要求,就是root宇宙到其他宇宙的时间不能超过k。人们想知道最少要建多少条通道。
20%的数据保证N<=10^4
40%的数据保证N<=10^5
100%的数据保证N<=500000,k<=20000
若干颗环套树。
显然可以贪心,由1往外连边必定是最优的。
树的部分类似拓扑,O(n)可以搞定
环的部分,每次枚举一个起点。预处理一个 nx[i] ,表示往i染色后下一个需要染色的点。显然直接往后跳就可以了。
维护并查集,将跳过的点并在一起,使得每个点只会被跳过一次。
按秩合并,维护到最后一个的距离。一个点到当前集合最右点的距离表示为其祖先节点的tag之和,十分容易维护。
1) tarjan忘记判断 vis 标记。
2) lt不应该直接赋为 f ,而是 gf(f)
SID244770
#include
#include
#include
#define INF (1<<30)
#define maxn 1000100
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
using namespace std;
int to[maxn],vis[maxn];
int S[maxn],on[maxn],inS[maxn],d[maxn],ac[maxn],dep[maxn],mor[maxn];
int Q[maxn],L,R;
int n,k,ans;
int nx[maxn],Li[maxn],fst[maxn];
int fa[maxn];
int tag[maxn],cnt[maxn],last[maxn],rk[maxn];
int gf(int x,int &dis) {
dis+=tag[x];
if (fa[x]==0) return x;
return fa[x]=gf(fa[x],dis);
}
int merge(int x,int y) {
if (rk[x]<=rk[y]) {
fa[x]=y;
tag[x]+=cnt[y]+1;
cnt[y]+=cnt[x]+1;
if (rk[x]==rk[y]) rk[y]++;
return y;
} else {
fa[y]=x;
tag[x]+=cnt[y]+1;
tag[y]-=tag[x];
cnt[x]+=cnt[y]+1;
last[x]=max(last[y],last[x]);
return x;
}
}
void tarjan(int x) {
inS[x]=vis[x]=1;
S[++S[0]]=x;
if (inS[to[x]]==1) {
int tmp=S[0];
do {
on[S[tmp]]=1;
} while (S[tmp--]!=to[x]);
} else
if (vis[to[x]]==0)tarjan(to[x]);
S[S[0]--]=0;
inS[x]=0;
}
void solveTree() {
for (int i=1; i<=n; i++) if (vis[i]==0) tarjan(i);
for (int i=1; i<=n; i++) {
if (d[i]==0) Q[++R]=i;
mor[i]=-1;
}
mor[1]=k;
while (Lint a=Q[++L];
if (mor[a]==-1 && on[a]==0) mor[a]=k-1,ans++;
if ((--d[to[a]])==0 && on[a]==0) Q[++R]=to[a];
mor[to[a]]=max(mor[to[a]],mor[a]-1);
}
}
void mkNext() {
int t=k;
for (int j=1; j<=Li[0]*2; j++) {
while (t0]*2 && (ac[t]==1 || tif (tint main() {
freopen("parralle.in","r",stdin);
freopen("parralle.out","w",stdout);
cin>>n>>k;
int x,y;
for (int i=1; i<=n; i++) {
scanf("%d %d",&x,&y);
to[x]=y,d[y]++;
}
solveTree();
for (int i=1; i<=n; i++) {
if (on[i]) {
int t=i,bz=0,mxx=i,mxMor,mi=INF,ad,lt,f,dis;;
Li[0]=0;
do{
Li[++Li[0]]=t;
on[t]=0;
if (t==1) bz=Li[0];
t=to[t];
} while (on[t]);
memcpy(Li+Li[0]+1,Li+1,Li[0]*4);
mxMor=-1;
for (int p=1; p<=Li[0]*2; p++) {
mxMor=max(mxMor-1,mor[Li[p]]);
if (mxMor>=0) ac[p]=1; else ac[p]=0;
}
mkNext();
if (bz) {
nx[bz]=nx[bz+Li[0]]=INF;
for (int z=bz+k+1; z<=Li[0]*2; z++) {
if (ac[z]==0) {
nx[bz]=z;
break;
}
}
for (int z=bz+Li[0]+k+1; z<=Li[0]*2; z++) {
if (ac[z]==0) {
nx[bz+Li[0]]=z;
break;
}
}
}
for (int z=1; z<=Li[0]*2; z++) cnt[z]=0,last[z]=z,fa[z]=0,tag[z]=0,rk[z]=0;
fst[Li[0]*2+1]=INF;
for (int z=Li[0]*2; z; z--) fst[z]=(ac[z]==0)?z:fst[z+1];
for (int st=1; st<=Li[0]; st++) {
ad=0,lt=0;
for (int j=fst[st]; j0];) {
dis=0,f=gf(j,dis);
ad+=dis+1;
j=nx[last[f]];
if (lt!=0 && j<=Li[0]) lt=merge(lt,f);
}
mi=min(ad,mi);
if (mi==1) break;
}
ans+=mi;
}
}
cout<