poj 2763 : 真的好傻逼的错误。
先讲题意:
先刷入 n q cur;
然后输入 n-1 条边,构成一棵树
然后q 行,每行一个操作: op 若op = 1,则输入 pos,w , 代表将上面的第pos条边的权值修改为w;
若op=0, 输入to,则代表将cur 移动到to这个位置,并输出最短的距离。
有人说是很标准的树链剖分, 但这题用lca 也能做一做。时间复杂度有点糟TUT
其实询问就是普通的lca 查询。
但是更新会有点麻烦。 其实并不复杂。
要修改第i 条边,则 tot =2*i 和2 * i+1。 修改完之后要怎么更新呢???
仔细想想,其实我们最朴素的想法就可以实现:我们直接往更深的地方更新dir即可。 因为dir 存的是到root的距离,然后我们把改变的边的终点找出来,往下更新即可,但是更新的时候呢,只能往下,也就是只能往rmq更大的节点更新。 !!!!!!!!!!错误来了,对于节点i 的深度我居然特么写成了 rmq[i],真的好蠢啊,rmq长度为2*MAXN,兄弟,rmq 对应的是F ,怎么能直接就写成rmq[i]啊,我真是醉了,完了自己debug了一下午+一晚上。 i的深度肯定得写成 rmq[first[i]]。
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int MAXN=100010;
int rmq[2*MAXN]; //rmq数组, 记录每个节点在树中的深度
struct ST{
int mm[2*MAXN];
int dp[2*MAXN][20]; //dp 直接存下标
void init(int n){
mm[0]=-1;
for(int i=1;i<=n;i++){
mm[i]=((i&(i-1))==0)? mm[i-1]+1 :mm[i-1]; //???莫名其妙定义长度
dp[i][0]=i; //初始化dp数组
}
for(int j=1;j<=mm[n];j++) //可以看出来mm[n] 就是一个长度为n的序列j的最大值
for(int i=1;i+(1<<j)-1 <=n ; i++){
if(rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1] ]){
dp[i][j]=dp[i][j-1];
}
else
dp[i][j]=dp[i+(1<<(j-1))][j-1];
}
}
int query(int a,int b){ //这个询问返回的是位置pos, F[pos]才是值
if(a>b)
swap(a,b);
int k=mm[b-a+1];
if(rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]] ){
return dp[a][k];
}
else
return dp[b-(1<<k)+1][k];
}
};
struct Edge{
int to,next;
int w; // 用w来记录权值:即到根部的距离。
};
Edge edge[MAXN*2];
int tot,head[MAXN];
int F[MAXN*2]; // 按顺序存储节点 ,下标从1开始,长度为2*n-1
int first[MAXN]; // i在F中第一次出现的位置
int cnt;
int fa[MAXN]; // 记录每个点的父亲节点,以此来找路径会特别方便
int w[MAXN];
int dir[MAXN];
ST st;
void addedge(int u,int v,int w){ //无向边自然加两次
edge[tot].to=v;
edge[tot].w=w;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int pre,int dep){ //u起点,dep表示深度
F[++cnt] = u; // cnt就是这个节点的下标
fa[u]=pre;
rmq[cnt]=dep; //rmq记录节点在树中的深度
// printf("dfs:%d %d %d %d\n",u,dep,rmq[c],pre);
first[u]=cnt; //这个first 不会被更新掉,因为我们把continue了回去的路
for(int i=head[u];i!=-1; i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
dir[v]=dir[u] + edge[i].w;
dfs(v,u,dep+1);
F[++cnt] = u; //虽然continue回去的路,但是询问叶子节点还要返回:1-2-1 --....
rmq[cnt] = dep;
}
}
void LCA_init(int root ,int node_num){ //查询LCA前的初始化
cnt=0;
dfs(root,root,0);
st.init(2*node_num-1); // 注意 ,这里dp的不是n,而是对F进行dp
}
int query_lca(int u,int v){ //查询lca(u,v)
return F[st.query(first[u],first[v])];
}
bool flag[MAXN];
void work(int u,int pre,int cnt){
dir[u]+=cnt;
for(int i=head[u];i!=-1; i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
if(rmq[first[u] ]<rmq[first[v]])
work(v,u,cnt);
}
}
void init(){
tot=0;
memset(head,-1,sizeof(head));
memset(flag,0,sizeof(flag));
// memset(rmq,0,sizeof(rmq));
}
int main(){
//freopen("1.txt","r",stdin);
int n,q,cur;
while(~scanf("%d %d %d",&n,&q,&cur)){
init();
for(int i=1;i<n;i++){
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
addedge(a,b,c); // 第i条边 为edge[(i-1)*2] 和edge[(i-1)*2+1]
addedge(b,a,c);
flag[b]=true;
}
int root;
for(int i=1;i<=n;i++)
if(!flag[i]){ //某个根
root=i;
break;
}
// mem()
dir[root]=0;
// printf("root=%d\n",root);
LCA_init(root,n);
while(q--){
int op;
scanf("%d",&op);
if(op==0){
int to;
scanf("%d",&to);
int end=query_lca(cur,to);
__int64 ans=0;
// printf("%d %d %d\n",dir[cur],dir[to],dir[end]);
ans=(__int64)dir[cur]+dir[to]-2*dir[end];
cur=to;
printf("%I64d\n",ans);
}
else {
int i,tem;
scanf("%d %d",&i,&tem);
int change=tem-edge[(i-1)*2].w;
edge[(i-1)*2].w=edge[ ((i-1)*2)+1 ].w=tem;
int u=edge[(i-1)*2].to,v=edge[(i-1)*2+1].to;
// printf("u,v:%d %d %d %d\n",u,v,rmq[first[u]],rmq[first[v]]);
if(rmq[first[u]]<rmq[first[v]]) swap(u,v);
work(u,v,change);
//我们用dir 记录到根节点的距离 ,所以往深度大的节点改动就行了,深度小的本身就不改变
}
}
}
return 0;
}
http://acm.hdu.edu.cn/showproblem.php?pid=3078
题意:
给一个n,q
然后给出n个节点的权值
接下来n-1 行,每行给一条无向边连接两个节点。
然后q行
每行一个操作:
输入 三个数 a,b,c 若a=0 ,则将点b 的权值改为c
若a!=0 ,则操作是询问 从 b -c 路径上所有点的权值 第K大是多少
首先得明确一个观点:
这种题目 操作中加入了修改,所以你修改会影响到下面的询问,讲道理会用在线算法。
对于这个题:我一开始想用一个 set去存每一个节点到根节点的路径。 因为N=8W ,二维数组开不下来。 但是set 不可以放重复的元素。 然后我就卡住了。 想了半天觉得 不可能说用一个东西来存路径,因为内存必然不够。
但是我们只要知道路径,在线的提取出来不就好了吗?
我们可以多开一个数组pre, pre[i]就代表 i的上一个节点是多少, 当我们建好树之后, 如果询问 i 和 j的路径第K大的节点。我们就可以 k= lca(i,j)
然后我们吧 i ->k 和 j->k 路径上的点的权值都记录在数组里,就可以找到第k大点了
#include <cstdio>
#include <cmath>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
#include <sstream>
#include <queue>
#include <typeinfo>
#include <fstream>
#include <map>
#include <stack>
typedef long long ll;
using namespace std;
const int MAXN=80010;
int rmq[2*MAXN]; //rmq数组, 记录每个节点在树中的深度
struct ST{
int mm[2*MAXN];
int dp[2*MAXN][20]; //dp 直接存下标
void init(int n){
mm[0]=-1;
for(int i=1;i<=n;i++){
mm[i]=((i&(i-1))==0)? mm[i-1]+1 :mm[i-1]; //???莫名其妙定义长度
dp[i][0]=i; //初始化dp数组
}
for(int j=1;j<=mm[n];j++) //可以看出来mm[n] 就是一个长度为n的序列j的最大值
for(int i=1;i+(1<<j)-1 <=n ; i++){
if(rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1] ]){
dp[i][j]=dp[i][j-1];
}
else
dp[i][j]=dp[i+(1<<(j-1))][j-1];
}
}
int query(int a,int b){ //这个询问返回的是位置pos, F[pos]才是值
if(a>b)
swap(a,b);
int k=mm[b-a+1];
if(rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]] ){
return dp[a][k];
}
else
return dp[b-(1<<k)+1][k];
}
};
struct Edge{
int to,next;
int w; // 用w来记录权值:即到根部的距离。
};
Edge edge[MAXN*2];
int tot,head[MAXN];
int F[MAXN*2]; // 按顺序存储节点 ,下标从1开始,长度为2*n-1
int first[MAXN]; // i在F中第一次出现的位置
int cnt;
int fa[MAXN]; // 记录每个点的父亲节点,以此来找路径会特别方便
int w[MAXN];
ST st;
void init(){
tot=0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v){ //无向边自然加两次
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int pre,int dep){ //u起点,dep表示深度
F[++cnt] = u; // cnt就是这个节点的下标
fa[u]=pre;
rmq[cnt]=dep; //rmq记录节点在树中的深度
first[u]=cnt; //这个first 不会被更新掉,因为我们把continue了回去的路
for(int i=head[u];i!=-1; i=edge[i].next){
int v=edge[i].to;
if(v==pre) continue;
// dir[v]=dir[u] + edge[i].w;
dfs(v,u,dep+1);
F[++cnt] = u; //虽然continue回去的路,但是询问叶子节点还要返回:1-2-1 --....
rmq[cnt] = dep;
}
}
void LCA_init(int root ,int node_num){ //查询LCA前的初始化
cnt=0;
dfs(root,root,0);
st.init(2*node_num-1); // 注意 ,这里dp的不是n,而是对F进行dp
}
int query_lca(int u,int v){ //查询lca(u,v)
return F[st.query(first[u],first[v])];
}
bool flag[MAXN];
int main(){
// freopen("1.txt","r",stdin);
int n,q;
while(~scanf("%d %d",&n,&q)){
init();
memset(flag,0,sizeof(flag));
for(int i=1;i<=n;i++)
scanf("%d",&w[i]);
for(int i=1;i<n;i++){
int u,v;
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
flag[v]=1;
}
int root;
for(int i=1;i<=n;i++)
if(!flag[i]){ //某个根
root=i;
break;
}
LCA_init(root,n);
int ans[MAXN];
while(q--){
int f,a,b;
scanf("%d %d %d",&f,&a,&b);
if(f==0){
w[a]=b;
continue;
}
int u=a;
int v=b;
int end=query_lca(u,v);
int len=0;
while(u!=end){
// printf("fa=%d %d %d\n",fa[u],w[u],w[fa[u]]);
ans[len++]=w[u];
u=fa[u];
}
while(v!=end){
ans[len++]=w[v];
v=fa[v];
}
ans[len++]=w[end];
sort(ans,ans+len);
// printf("ans:");
// for(int i=0;i<len;i++)
// printf("%d ",ans[i]);
// cout<<endl;
if(f>len)
printf("invalid request!\n");
else
printf("%d\n",ans[len-f]);
}
}
return 0;
}