POJ1986:
链接:http://poj.org/problem?id=1986
题意:首先用奇怪的方式给出图,然后乱搞下把图弄成习惯的方式。题目要求的是树上两点之间距离,利用dfs+lca,求出每个点到树根的距离,然后再减去最近公共祖先到树根的距离的两倍
LCA:
LCA处理的方式是后序遍历,程序大概的流程:
{
1.判断有关于当前点的询问能否解决(另一个点访问过),如果可以将信息放入最近公共祖先的vector里
2.遍历当前点的所有子节点
3.处理所有公共祖先为当前点的询问
}
代码:
<span style="font-family:Microsoft YaHei;">#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define Ror(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define mp make_pair
#define pb push_back
#define inf 0x3f3f3f3f
void RI (int& x){
x = 0;
char c = getchar ();
while (c == ' '||c == '\n') c = getchar ();
bool flag = 1;
if (c == '-'){
flag = 0;
c = getchar ();
}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar ();
}
if (!flag) x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}
const int maxn = 50050;
const int maxm = 100050*2;
int fa[maxn];
struct Side{
int v,w,next;
}side[maxm];
int top,node[maxn];
void add_side(int u,int v,int w){
side[top] = (Side){v,w,node[u]};
node[u] = top++;
}
bool vis[maxn];
struct ASK{
int u,v,pos;
};
vector<ASK>ask[maxn],get[maxn];
int mon[maxn];
int ans[maxn];
int dis[maxn];
int find(int x){
if(x == fa[x])return x;//cout<<x;
return fa[x] = find(fa[x]);
}
void tarjan(int u,int dd){
vis[u] = 1;//cout<<u<<' '<<dd<<endl;
dis[u] = dd;
int siz = ask[u].size();
For(i,0,siz){
int v = ask[u][i].v;
if(vis[v]){
int t = find(v);
//cout<<u<<' '<<v<<' '<<t<<endl;
get[t].pb((ASK){u,v,ask[u][i].pos});
}
}
for(int i = node[u];i != -1;i = side[i].next){
int v = side[i].v;
if(vis[v] == 0){
tarjan(v,dd + side[i].w);
fa[v] = u;
}
}
siz = get[u].size();
For(i,0,siz){
//cout<<get[u][i].u<<' '<<get[u][i].v<<' '<<dis[get[u][i].u]<<' '<<dis[get[u][i].u]<<' '<<dis[u]<<endl;
ans[get[u][i].pos] = dis[get[u][i].u] + dis[get[u][i].v] - 2*dis[u];
}//cout<<u<<endl;
}
struct T{
int v,dis;
}mm[maxn][4];//n s e w
void add(int u,int v,int w,int pos){
if(mm[u][pos].v == -1){
mm[u][pos].v = v;
mm[u][pos].dis = mm[v][pos^1].dis = w;
mm[v][pos^1].v = u;
return;
}
else if(mm[u][pos].dis > w){
mm[v][pos].v = mm[u][pos].v;
mm[v][pos].dis = mm[mm[u][pos].v][(mm[u][pos].v)^1].dis = mm[u][pos].dis - w;
mm[mm[u][pos].v][mm[u][pos].v^1].v = u;
mm[u][pos].v = v;
mm[u][pos].dis = mm[v][(pos^1)].dis = w;
mm[v][pos^1].v = u;
return ;
}
else {
add(mm[u][pos].v,v,w - mm[u][pos].dis,pos);
}
}
int main(){
//freopen("test.txt","r",stdin);
int n,m;
while(~scanf("%d%d",&n,&m)){
For(i,1,n+1){
For(j,0,4){
mm[i][j].v = -1;
}
}
while(m --){
int u,v,w;
char dir[8];
RIII(u,v,w);
scanf("%s",dir);
switch(dir[0]){
case 'N':{
add(u,v,w,0);
break;
}
case 'S':{
add(u,v,w,1);
break;
}
case 'E':{
add(u,v,w,2);
break;
}
case 'W':{
add(u,v,w,3);
break;
}
}
}
mem(node,-1);top = 0;
For(i,1,n+1){
For(j,0,4){
if(mm[i][j].v != -1)
add_side(i,mm[i][j].v,mm[i][j].dis),
add_side(mm[i][j].v,i,mm[i][j].dis);
}
}
For(i,1,n+1)ask[i].clear(),get[i].clear();
int q;
RI(q);
For(i,1,q+1){
int a,b;
RII(a,b);
ask[a].pb((ASK){a,b,i});
ask[b].pb((ASK){b,a,i});
}
mem(vis,0);
mem(dis,0);
For(i,1,n+1)fa[i] = i;
tarjan(1,0);
For(i,1,q+1)
cout<<ans[i]<<endl;
}
return 0;
}</span>
POJ3728:
链接:http://poj.org/problem?id=3728
题意:给出树,树的节点有权值(商品的售价),给出起、终点,从起点走向终点,从某处买入,然后卖出(只有一次,不能回头=。=),问最大收益。
思路:很麻烦,看的题解。。还各种纠结。。
维护四个数组:
1.从当前点到lca的最大收益up
2.从lca到当前点的最大收益down
3.从当前点到lca的最大权值mmax
4.从当前点到lca的最小权值mmin
这样在知道两点的最近公共祖先后就可以用这些值求出解。
初始化:
up和down初始化为0,mmax和mmin为各个点权值
更新:
在并查集寻找祖先的过程中更新,考虑下就可以写出来。。
代码:
<span style="font-family:Microsoft YaHei;">#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
using namespace std;
#define mem(a,b) memset((a),(b),sizeof((a)))
#define For(i,a,b) for(int (i)=(a);(i) < (b);(i)++)
#define Ror(i,a,b) for(int (i)=(a);(i) > (b);(i)--)
#define mp make_pair
#define pb push_back
#define inf 0x3f3f3f3f
void RI (int& x){
x = 0;
char c = getchar ();
while (c == ' '||c == '\n') c = getchar ();
bool flag = 1;
if (c == '-'){
flag = 0;
c = getchar ();
}
while (c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar ();
}
if (!flag) x = -x;
}
void RII (int& x, int& y){RI (x), RI (y);}
void RIII (int& x, int& y, int& z){RI (x), RI (y), RI (z);}
const int maxn = 100050;
const int maxm = 100050*2;
int fa[maxn];
struct Side{
int v,next;
}side[maxm];
int top,node[maxn];
void add_side(int u,int v){
side[top] = (Side){v,node[u]};
node[u] = top++;
}
bool vis[maxn];
struct Node{
int u,v,pos;
};
vector<Node>ask[maxn],get[maxn];
int mon[maxn];
int ans[maxn];
int mmin[maxn],mmax[maxn];///表示u-f的最 / w[i]
int up[maxn],down[maxn];
int find(int x){
if(x == fa[x])return x;//cout<<x;
int ff = fa[x];//if(x == 2)cout<<ff<<endl;
fa[x] = find(fa[x]);
up[x] = max(up[x],max(up[ff],mmax[ff] - mmin[x]));
down[x] = max(down[x],max(down[ff],mmax[x] - mmin[ff]));
mmax[x] = max(mmax[x],mmax[ff]);
mmin[x] = min(mmin[x],mmin[ff]);//if(x == 2)cout<<mmin[2]<<endl;
return fa[x];
}
void tarjan(int u){
vis[u] = 1;
int siz = ask[u].size();
For(i,0,siz){
int v = ask[u][i].v;
if(vis[v]){
int t = find(v);
int pos = ask[u][i].pos;//cout<<u<<pos<<endl;
if(pos > 0){
get[t].pb((Node){u,v,pos});
}
else get[t].pb((Node){v,u,-pos});
}
}
for(int i = node[u];i != -1;i = side[i].next){
int v = side[i].v;
if(vis[v] == 0){
tarjan(v);
fa[v] = u;
}
}
siz = get[u].size();
For(i,0,siz){//
int uu = get[u][i].u;
int vv = get[u][i].v;//if(u == 1)cout<<uu<<' '<<vv<<endl;
find(uu);find(vv);//cout<<up[uu]<<' '<<down[vv]<<' '<<mmax[vv]<<' '<<mmin[uu]<<endl;
ans[get[u][i].pos] = max(up[uu],max(down[vv],mmax[vv] - mmin[uu]));
}//cout<<u<<endl;
}
int main(){
//freopen("test.txt","r",stdin);
int n;
while(~scanf("%d",&n)){
mem(mon,0);
For(i,1,n+1){
RI(mon[i]);
}
mem(node,-1);top = 0;
mem(ans,0);
For(i,0,n-1){
int a,b;
RII(a,b);
add_side(a,b);
add_side(b,a);
}
For(i,1,n+1)ask[i].clear(),get[i].clear();
int q;
RI(q);
For(i,1,q+1){
int a,b;
RII(a,b);
ask[a].pb((Node){a,b,i});
ask[b].pb((Node){b,a,-i});
}
For(i,1,n+1){
fa[i] = i;
mmin[i] = mmax[i] = mon[i];
up[i] = down[i] = 0;
vis[i] = 0;
}
tarjan(1);
For(i,1,q+1)
cout<<max(ans[i],0)<<endl;
}
return 0;
}
</span>