bzoj 1791 DP

  首先对于一棵树我们可以tree_dp来解决这个问题,那么对于环上每个点为根的树我们可以求出这个树的一端为根的最长链,并且在tree_dp的过程中更新答案。那么我们对于环,从某个点断开,破环为链,然后再用DP来解决这个问题。

  备注:很久之前的一道题,刚转的c++,然后T了,也懒得改了。


/**************************************************************
Problem: 1791
User: BLADEVIL
Language: C++
Result: Time_Limit_Exceed
****************************************************************/

//By BLADEVIL
#include <cstdio>
#include <cstring>
#define maxn 1000010
#define LL long long

using namespace std;

LL n,time,l;
LL other[maxn<<1],last[maxn],pre[maxn<<1],dfn[maxn],low[maxn],vis[maxn],que[maxn],a[maxn],xx[maxn];
LL ans;
LL len[maxn<<1],max1[maxn],max2[maxn],sum[maxn],w[maxn],yy[maxn],maxlen[maxn];
LL save;

void getmin(LL &x,LL y)
{if (y<x) x=y;}

void connect(LL x,LL y,LL z) {
pre[++l]=last[x];
last[x]=l;
other[l]=y;
len[l]=z;
//if (y>x) printf("%d %d %lld\n",x,y,z);
}

void dfs(LL x,LL fa) {
dfn[x]=low[x]=++time;
for (LL q=last[x];q;q=pre[q]) {
if (other[q]==fa) continue;
if (!low[other[q]]) {
dfs(other[q],x);
getmin(low[x],low[other[q]]);
} else getmin(low[x],dfn[other[q]]);
}
if (low[x]!=dfn[x]) save=low[x];
}

void dp(LL x) {
memset(que,0,sizeof que);
LL h=0,t=1ll;
que[1]=x; vis[x]=1ll;
while (h<t) {
LL cur=que[++h];
for (LL q=last[cur];q;q=pre[q]) {
if (low[other[q]]==low[x]) continue;
if (vis[other[q]]) continue;
vis[other[q]]=vis[cur]+1ll;
que[++t]=other[q];
}
}
//for (LL i=1;i<=t;i++) printf("%d ",que[i]); printf("\n");
for (LL i=t;i;i--)
for (LL q=last[que[i]];q;q=pre[q]) {
if (low[other[q]]==low[x]) continue;
if (vis[other[q]]!=vis[que[i]]+1) continue;
//printf(" %d %d\n",que[i],other[q]);
if (max1[other[q]]+len[q]>max1[que[i]])
max2[que[i]]=max1[que[i]],max1[que[i]]=max1[other[q]]+len[q]; else
if (max1[other[q]]+len[q]>max2[que[i]]) max2[que[i]]=max1[other[q]]+len[q];
if (max1[other[q]]+max2[other[q]]>maxlen[que[i]])
maxlen[que[i]]=max1[other[q]]+max2[other[q]];
if (max1[que[i]]+max2[que[i]]>maxlen[que[i]])
maxlen[que[i]]=max1[que[i]]+max2[que[i]];
if (maxlen[que[i]]<maxlen[other[q]]) maxlen[que[i]]=maxlen[other[q]];
}
//for (LL i=1;i<=t;i++) printf("%d %lld %lld %lld\n",que[i],max1[que[i]],max2[que[i]],maxlen[que[i]]);
}

void solve(LL x) {
LL now=0ll;
dfs(x,-1);
//printf("%d",save);
if (save)
for (LL i=1;i<=n;i++) if (low[i]==save) x=i;
save=0;
/*if (xx[xx[x]]==x)
{
ans+=(yy[x]>yy[xx[x]])?yy[x]:yy[xx[x]];
return;
}*/
LL cur;
for (LL i=1;i<=n;i++) if (low[i]==low[x]) dp(i),cur=i;
//printf(" %lld\n",max1[x]);
LL t=1; a[t]=cur;
while (1) {
for (LL q=last[a[t]];q;q=pre[q]) {
now=(maxlen[a[t]]>now)?maxlen[a[t]]:now;
if (low[other[q]]!=low[x]) continue;
if (other[q]==a[t-1]) continue;
a[++t]=other[q]; sum[t]=len[q]; break;
}
if (a[t]==cur) break;
}
//printf(" %d ",x);
//for (LL i=1;i<=t;i++) printf(" %lld %d %d\n",max1[a[i]],a[i],sum[i]);
t--;
for (LL i=2;i<=t;i++) a[i+t]=a[i],sum[i+t]=sum[i];
t*=2;
//for (LL i=1;i<=t;i++) printf(" %lld %d %d\n",max1[a[i]],a[i],sum[i]);
for (LL i=1;i<=t;i++) sum[i]+=sum[i-1];
LL len=t>>1ll;
memset(que,0,sizeof que);
LL l=1,r=1; que[1]=1;
for (LL i=2;i<=t;i++) {
if (i-que[l]+1>len) l++;
w[i]=max1[a[que[l]]]+max1[a[i]]+sum[i]-sum[que[l]];
//printf("w[i]=%d",w[i]); printf(" %d %d\n",i,que[l]);
while (l<=r&&(max1[a[i]]-sum[i]>max1[a[que[r]]]-sum[que[r]])) r--;
que[++r]=i;
//for (LL i=l;i<=r;i++) printf("|%d ",que[i]); printf("\n");
}
for (LL i=1;i<=t;i++) now=(w[i]>now)?w[i]:now;
//printf(" %lld ",ans);
ans+=now;
}

int main() {
scanf("%d",&n);
for (LL i=1;i<=n;i++) scanf("%d%lld",&xx[i],&yy[i]);
for (LL i=1;i<=n;i++) {
if (xx[i]==i) continue;
if (xx[xx[i]]==i&&xx[i]>i) yy[i]=(yy[xx[i]]>yy[i])?yy[xx[i]]:yy[i],yy[xx[i]]=-1ll;
}
for (LL i=1;i<=n;i++) if (yy[i]!=-1) connect(i,xx[i],yy[i]),connect(xx[i],i,yy[i]);
for (LL i=1;i<=n;i++) if (!low[i]) solve(i);
//for (LL i=1;i<=n;i++) printf(" %d %d %d\n",i,low[i],dfn[i]);
printf("%lld\n",ans);
return 0;
}

bzoj 1791 DP,布布扣,bubuko.com

时间: 2024-12-15 01:54:52

bzoj 1791 DP的相关文章

bzoj 1222 DP

用w[i]表示在A中用了i的时间时在B中最少用多长时间,然后转移就可以了. 备注:这个边界不好定义,所以可以每次用一个cur来存储最优值,然后对w[i]赋值就可以了. /************************************************************** Problem: 1222 User: BLADEVIL Language: C++ Result: Accepted Time:2648 ms Memory:992 kb ***************

bzoj 3622 DP + 容斥

LINK 题意:给出n,k,有a,b两种值,a和b间互相配对,求$a>b$的配对组数-b>a的配对组数恰好等于k的情况有多少种. 思路:粗看会想这是道容斥组合题,但关键在于如何得到每个a[i]大于b的组数. 不妨从整体去考虑,使用$f[n][j]$代表前n个中有j组$a[i]>b[i]$,很容易得到转移式$f[n][j]=f[n-1][j]+f[n-1][j-1]*(cnt[n]-(j-1))$,其中$cnt[i]$为比a[i]小的b[]个数 但是仔细思考该式子含义会发现,$f[n][j

BZOJ 1791 岛屿(环套树+单调队列DP)

题目实际上是求环套树森林中每个环套树的直径. 对于环套树的直径,可以先找到这个环套树上面的环.然后把环上的每一点都到达的外向树上的最远距离作为这个点的权值. 那么直径一定就是从环上的某个点开始,某个点结束的. 把环拆成链,定义dp[i]表示第i个点为结束点的最远距离,显然有dp[i]=val[j]+sum[i]-sum[j-1]+val[i].显然可以用单调队列优化这个DP. 剩下的就是这样依次统计每个环套树的直径之和. 对于环套树上找环可以借鉴最小树形图找环的技巧. 首先将边定向,保证每个点的

bzoj 1791: [Ioi2008]Island 岛屿【基环树+单调队列优化dp】

我太菜了居然调了一上午-- 这个题就是要求基环树森林的基环树直径和 大概步骤就是找环->dp找每个环点最远能到达距离作为点权->复制一倍环,单调队列dp 找环是可以拓扑的,但是利用性质有更快好写的做法,就是像朱刘算法找环那样,按照输入的方向(i->to_i)打一圈标记,如果碰到同样标记就说明有环,这里注意我一开始没注意到的,从i点进入找到环不代表i点在环上,因为可能是6字形的,所以一定是环点的是找到的有同样标记的那个点,然后顺着这个点把环点都放进一个栈(其实不用,但是这样好写一些),顺着

BZOJ 2431 &amp; DP

题意:求逆序对数量为k的长度为n的排列的个数 SOL: 显然我们可以对最后一位数字进行讨论,判断其已经产生多少逆序对数量,然后对于前n-1位同样考虑---->每一个长度的排列我们都可以看做是相同的,因为它与最后一位的影响我们已经计算过了.那么就变成了一个好多维DP的过程... 不过我的方程感觉有点太直白,应该可以优化因为在BZ上都是卡时过去的...太慢了...大概状态还是有问题.... Code: /*===============================================

BZOJ 2101 DP+优化

思路: http://www.cnblogs.com/exponent/archive/2011/08/14/2137849.html f[i,i+len]=sum[i,i+len]-min(f[i+1,i+len],f[i,i+len-1]); 但题目把n出到5000,内存卡到64M,二维的状态存不下.. 其实,j这一维可以省掉.我们换个状态表示 f[i,i+len]=sum[i,i+len]-min(f[i+1,i+len],f[i,i+len-1]) 然后循环这样写: for len=1

BZOJ 1003 dp+最短路

1003: [ZJOI2006]物流运输 题意:m个码头,从码头1到码头m,连续n天都要运送货物.每一天的花费是总路线长度大小,但如果和前一天的路线不一样,要另处加上k元花费.而且有些码头有些天不能用,问这n天的最小费用. tags:菜鸡一开始真没想到是dp 求n天时最小花费,就要想到以天数为阶段进行规划.dp[i][j]表示第i天到第j天走同一条路线的花费,则f[i]=min( f[i], f[j]+dp[j+1][i]+k ). #include<bits/stdc++.h> using

【BZOJ 1791】 [Ioi2008]Island 岛屿

Description 你将要游览一个有N个岛屿的公园.从每一个岛i出发,只建造一座桥.桥的长度以Li表示.公园内总共有N座桥.尽管每座桥由一个岛连到另一个岛,但每座桥均可以双向行走.同时,每一对这样的岛屿,都有一艘专用的往来两岛之间的渡船. 相对于乘船而言,你更喜欢步行.你希望所经过的桥的总长度尽可能的长,但受到以下的限制. • 可以自行挑选一个岛开始游览. • 任何一个岛都不能游览一次以上. • 无论任何时间你都可以由你现在所在的岛S去另一个你从未到过的岛D.由S到D可以有以下方法: o 步

bzoj 1009 DP 矩阵优化

原来的DP: dp[i][j]表示长度为i的合法串,并且它的长度为j的后缀是给定串的长度为j的前缀. 转移: i==0 dp[0][0] = 1 dp[0][1~m-1] = 0 i>=1 dp[i][0] = dp[i-1][0]*10-dp[i-1][m-1] dp[i][1] = dp[i-1][0]-(a[m]==a[1])*dp[i-1][m-1] dp[i][2] = dp[i-1][1]-(a[m-1~m]==a[1~2])*dp[i-1][m-1] dp[i][3] = dp[i