825 G. Tree Queries dp思维

题目链接: http://codeforces.com/contest/825/problem/G

题意:

给你一棵树,树上的结点开始全部为白色,现在给你两种操作。
1 1 1 x x x 表示将结点 x x x 染成黑色(保证第一次操作一定为类型①)
2 2 2 x x x 表示询问从结点 x x x 到任意一个黑色的点的路径上经过的最小结点的下标。

做法:

你会发现如果你染了几个结点,那么这些结点之间的路径上的所有结点的最小值,都可以作为下一次询问任何节点的可能答案。 即如果 P 2 P_2 P2 P 3 P_3 P3 都为黑色,那么我询问 P 4 P_4 P4 的时候, P 2 P_2 P2 P 3 P_3 P3之间路径上的结点最小值 a n s m i n ans_{min} ansmin 是可能作为答案的,那么另一种可能是什么呢,是 P 4 P_4 P4 本身到某个黑色 P i P_i Pi 的最小下标。

这个我们可以这么来表示呢,其实我们完全可以 以开始给你的第一个染黑的结点为根节点 r o o t root root ,之后每次染色的时候,将被染色结点 x x x 到根节点 r o o t root root的最小值 d p [ x ] dp[x] dp[x]来更新上面提到的 a n s m i n ans_{min} ansmin 。在查询的时候,将被查询结点 u u u d p [ u ] dp[u] dp[u] a n s m i n ans_{min} ansmin 取最小值即可。

为什么可以这样做呢,因为对于任意两个黑色的结点,在到达 r o o t root root 之前不管是否有点相交,都不会对结果造成影响,依旧可以保证 a n s m i n ans_{min} ansmin 是正确的(这个可以画个图自己感受一下),所以正确性是可以证明的。

代码

#include 
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define rep_e(i,u) for(int i=head[u];~i;i=nex[i])
using namespace std;
typedef long long ll;
const int maxn=1000005;
const int maxm=2000005;
int n,dp[maxn],vis[maxn],q;
int head[maxn],to[maxm],nex[maxm],cnt;

void add(int u,int v){
    to[cnt]=v;nex[cnt]=head[u];
    head[u]=cnt++;
}
void dfs(int u,int f){
    dp[u]=min(u,dp[f]);
    rep_e(i,u){
        int v=to[i];
        if(v==f) continue;
        dfs(v,u);
    }
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&q);
    rep(i,2,n){
        int x,y; scanf("%d%d",&x,&y);
        add(x,y); add(y,x);
    }
    int op,x;
    scanf("%d%d",&op,&x);
    dp[0]=1e9;
    x=x%n+1;
    dfs(x,0);
    int last=0,mi=x;
    rep(i,2,q){
        scanf("%d%d",&op,&x);
        x=(x+last)%n+1;
        if(op==1){
            mi=min(mi,dp[x]);
        }
        else{
            last=min(mi,dp[x]);
            printf("%d\n",last);
        }
    }
    return 0;
}

你可能感兴趣的:(dp,思维)