2024 ICPC ShaanXi Provincial Contest C. Seats
题目链接:
正经题意:
给出长为 n 数组 ai, 你需要找到长为 n 的数组 bi(1 ≤ i ≤ n, 1 ≤ bi ≤ 2n) 满足 ∀i ̸= j, bi ̸= bj 且 ∀i, bi = i 或 bi = ai。最大化 bi = ai 的个数并输出最大值。
简而言之:
其实就是在基环树森林和森林中找两个东西
1.成环的结点个数
2.森林中所有以 (>n <=n*2)为根的最长路的和
思路:
进行拓扑排序,可以轻易分割基环树森林和森林,不在拓扑序列中的一定成环!可以直接解决我们要的第一个东西。
第二个东西只需要记录下逆向边,从每个结点出发寻找最长路即可
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e6+26;
typedef long long LL;
int e[N],ne[N],h[N],idx;
int d[N];int dist[N];int dd[N];
int q[N];int p[N];int p2[N];int p3[N];
int p4[N];
int e2[N],ne2[N],h2[N],idx2;
void add1(int a,int b){
e[idx] = b,ne[idx] = h[a],h[a] = idx++;
}
void add2(int a,int b){
e2[idx2] = b,ne2[idx2] = h2[a],h2[a] = idx2++;
}
int n,m; int tt=-1,hh=0;
void topsort(){
for(int i = 1;i<=n;i++){
if(d[i]==0) q[++tt] = i;
}
while(hh<=tt){
int head = q[hh++];
if(h[head]==-1) continue;
int j = e[h[head]];
d[j]--;
if(d[j]==0) q[++tt] = j;
}
}
int main(){
scanf("%d",&n);
memset(h,-1,sizeof h);
memset(h2,-1,sizeof h2);
for(int i = 1;i <= n;i++){
int x;
scanf("%d",&x);
if(i==x) p2[i] = 1;//标记是否是自环
add1(i,x);//正边
add2(x,i);//逆边
d[x]++;
}
topsort();//拓扑排序找出不是环的结点
int cott=0;
for(int i = 0;i<=n*2-1;i++){
if(q[i]!=0) p3[q[i]]++;
}
for(int i = 1;i<=n;i++){
if(p3[i]) cott++;
}
int ccoo=n-cott;
if(tt==-1) ccoo = n;
LL sum = ccoo;
for(int i = n+1;i<=2*n;i++){
queue<int> qq;
qq.push(i);
int maxa = 0;
while(qq.size()){
int x = qq.front();
qq.pop();
if(h2[x]==-1) continue;
for(int j = h2[x];j!=-1;j=ne2[j]){
int k = e2[j];
if(x == k) break;
if(p3[k]==0) break;
dist[k] = max(dist[k],dist[x]+1);
maxa = max(dist[k],maxa);
qq.push(k);
}
}
sum += maxa;
}
printf("%d",sum);
return 0;
}