「IOI2008」Islands 题解报告

先占个坑,逊完了再补...
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=1e7+2;
ll n,tot,cnt,ans,anss,st,ans2,ans3;
ll w[N<<1],fir[N],to[N<<1],nxt[N<<1],vis[N],v2[N],r[N],d[N],dp[N<<1],s[N];
void add(ll x,ll y,ll z){
w[++tot]=z;
to[tot]=y;
nxt[tot]=fir[x];
fir[x]=tot;
}
bool dfs(ll u,ll v){//dfs找环
if(vis[u]==1){//找到衔接点 :走到走过的节点
vis[u]=2;//遍历到2次
r[++cnt]=u;//环上第cnt个点
v2[u]=1;//环上的点
return 1;
}
vis[u]=1;//维护访问数组
for(ll i=fir[u];i;i=nxt[i]){
//v:刚走过的边
//i:现在走的边
//((v-1)^1)+1:与x相邻的数,指向双向边的另一条
//若v为偶,值为较小奇数,即v-1
//若v为奇,值为较大偶数,即v+1
//等同于v+((v&1)?1:-1)
if(i==((v-1)^1)+1) continue; //通向父亲
//循环到刚才走过的那条边,跳过
if(dfs(to[i],i)){//当前节点在环上
if(vis[u]!=2) {//当前节点不是衔接点
r[++cnt]=u;//环上的cnt号点原始编号
v2[u]=1;//u节点所在的基环树 1:走过
s[cnt]=s[cnt-1]+w[i];//前缀
return 1;
} else {//是衔接点
s[st-1]=s[st]-w[i];//前缀
//将多走的一条边删掉
return 0;
}
}
}
return 0;
}
void trdp(ll u){//case1//dp直径
v2[u]=1;
for(ll i=fir[u];i;i=nxt[i]){
ll v=to[i];
if(v2[v])
continue;
trdp(v);
ans=max(ans,d[u]+d[v]+w[i]);
d[u]=max(d[u],d[v]+w[i]);
}
}
ll solve(ll rt){
st=cnt+1,ans2=0,ans3=0;
dfs(rt,0);
//环上节点数量cnt-st+1
for(ll i=st;i<=cnt;i++){
ans=0;
trdp(r[i]);
ans2=max(ans2,ans);//case 1:在一棵子树上
dp[i+cnt-st+1]=dp[i]=d[r[i]];//二倍,断环为链
s[i+cnt-st+1]=s[i+cnt-st]+s[i]-s[i-1];
//节点i对应的另一个节点即为i+cnt-st+1
}
deque<ll> q;//双端队列
for(ll i=st;i<=2*cnt-st+1;i++){
while(!q.empty()&&q.front()<=i-cnt+st-1) q.pop_front();
if(!q.empty()) ans3=max(ans3,dp[i]+dp[q.front()]+s[i]-s[q.front()]);
while(!q.empty()&&dp[q.back()]-s[q.back()]<=dp[i]-s[i]) q.pop_back();
q.push_back(i);
}//case2
return max(ans2,ans3);//取优解
}
int main(){
scanf("%lld",&n);
for(ll x=1,y,z;x<=n;x++){
scanf("%lld%lld",&y,&z);
add(x,y,z),add(y,x,z);
}//输入
for(ll i=1;i<=n;i++){
if(!v2[i])//保证遍历每个节点
anss+=solve(i);
}
printf("%lld",anss);
return 0;
}

浙公网安备 33010602011771号