20190126考试

T1

给定一颗N个节点的树,定义两点距离为他们之间路径中边权最小值。

Q次询问K,V,询问到V距离>=K的点有多少(不含V)

输入

第一行两个整数N,Q。

接下来N-1行,每行3个整数u,v,w表示u,v之间有条路径,长为w

接下来Q组询问,每组询问2个整数k,V

输出

Q行回答询问

样例输入

4 3
1 2 3
2 3 2
2 4 4
1 2
4 1
3 1

样例输出

3
0
2

提示 对于30%的数据,1≤N,Q≤1000。

对于70%的数据,1≤N≤2000,Q≤10^5。

对于100%的数据,1≤N,Q≤10^5, 1≤w,K≤10^9.

解答

我们考虑并查集维护。把所有询问离线下来,把询问的值从大到小排序,然后再把每一条边也从大到小排序。处理每个询问的时候,把每一条边从大到小加入并查集,知道边权小于查询的边权。这样并查集中的点数,就是答案。注意并查集在合并的时候,合并权重不可以这样写void unionn(int x,int y){fa[find(y)]=find(x);wei[find(x)]+=wei[find(y)];},因为前面y经过路径压缩已经把父节点改为fa[x]了,所以后面更新权重的时候就不能那样做。 代码:

# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
const int MAXN = 100010;
const int MAXM = MAXN-1;
const int INF = 0x3f3f3f3f;
struct edge{int f,t,w,next;bool operator<(const edge &e2)const {return w>e2.w; }} edges[MAXM<<1];
int n ,Q;
int read(){
    int x = 0,f = 1;
    static char c = getchar();
    while(c<'0'||c>'9'){ if(c=='-')f = -1;c = getchar(); }
    while(c>='0'&&c<='9'){ x = (x<<1)+(x<<3)+c-'0';c = getchar(); }
    return x*f;
}
struct query{int len,root,id,ans;}querys[(int)1e5+10];
int fa[MAXN];
int wei[MAXN];
int find(int x){
    return  fa[x]==x?x:fa[x] = find(fa[x]);
}
void unionn(int x,int y){
    int fx = find(x);int fy = find(y);
    fa[fy] = fx;
    wei[fx]+=wei[fy];
}
bool cmp1(const query&q1,const query&q2){
    return q1.len>q2.len;
}
bool cmp2(const query&q1, const query &q2){
    return q1.id<q2.id;
}
int main(){
    n = read();Q = read();
    int f,t,w;
    for(int i = 1;i<=n-1;i++){
        f = read(), t = read(),w = read();
        edges[i].f = f;edges[i].t = t; edges[i].w = w;
    }
    int root,K;
    for(int i = 1;i<=Q;i++){
        K = read(),root = read();
        querys[i].len = K;querys[i].root = root;
        querys[i].id = i;
    }
    sort(querys+1,querys+Q+1,cmp1);
    sort(edges+1,edges+n);
    int pos = 1;
    for(int i = 1;i<=n;i++)fa[i] = i,wei[i] = 1;
    for(int i = 1;i<=Q;i++){
        while(pos<n&&edges[pos].w>=querys[i].len)unionn(edges[pos].f,edges[pos].t),pos++;
        querys[i].ans = wei[find(querys[i].root)]-1;
    }
    sort(querys+1,querys+Q+1,cmp2);
    for(int i = 1;i<=Q;i++){
        printf("%d\n",querys[i].ans);
    }
    return 0;
}

T2

题面

给定一个迷宫,构成一棵有根树,你开始在根节点,出口是每个叶子节点,L可以在每个出口放一个守卫,每1个单位时间内,你和守卫都可以移动到相邻的一个点,如果某一时刻 守卫与你相遇了(在边上或点上均算),则你将被抓住。问为了保证抓住你,L最少需要几个守卫。

输入

第1行包含2个用空格分开的正整数n、K,表示有n个节点,K表示根节点编号

接下来n-1行,每行2个整数u,v,表示u到v有条路径

输出

输出1个整数。

样例输入

7 1
1 2
1 3
3 4
3 5
4 6
5 7

样例输出

3

提示 N<=100000

解答

其实考试的时候算是想到半个正解了的吧。求出根节点到每个节点的深度dep[x],再求出叶节点到每个节点的深度dep2x,因为要拦截,所以对于每个点必须要$dep2[x]>dep[x]$。你可看下图,去了最小之后,就相当于把一些分枝砍掉了。然后对于一个节点要防到,所以在假设对方不存在的情况下,必须保卫者先走到才行。所以从上到下搜对于dep2[x]<=dep[x]的点,需要一个保卫,就ans++就行了 title

# include <cstdio>
# include <cstring>
const int MAXN = 100010;
const int MAXM = MAXN-1;
const int INF = 0x3f3f3f3f;
struct edge{int t,w,next;bool operator<(const edge &e2)const {return w>e2.w; }} edges[MAXM<<1];
int head[MAXN],top  = 1;
int n ,K;
int read(){
    int x = 0,f = 1;
    static char c = getchar();
    while(c<'0'||c>'9'){ if(c=='-')f = -1;c = getchar(); }
    while(c>='0'&&c<='9'){ x = (x<<1)+(x<<3)+c-'0';c = getchar(); }
    return x*f;
}
void add(int f,int t,int w) {
    edges[++top].next = head[f];
    edges[top].t = t;
    edges[top].w = w;
    head[f] = top;
}
int dep1[MAXN],dep2[MAXN];
bool vis[MAXN];
inline int min(int a,int b){return a<b?a:b;}
void dfs(int x,int fa){
    vis[x] = true;
    dep1[x] = dep1[fa]+1;
    dep2[x] = INF;
    for(int i = head[x];i;i = edges[i].next){
        int t = edges[i].t;
        if(vis[t])continue;
        dfs(t,x);
        dep2[x] = min(dep2[t]+1,dep2[x]);
    }
    if(dep2[x]==INF)dep2[x] = 1;
}
int ans;
bool vis2[MAXN];
void work(int x){
    vis2[x] = true;
    if(dep2[x]<=dep1[x]){
        ans++;
        return;
    }
    for(int i = head[x];i;i = edges[i].next){
        int  t = edges[i].t;
        if(vis2[t])continue;
        work(t);
    }
}
int main(){
    n = read();K = read();
    int f,t;
    for(register int i = 1;i<=n-1;i++){
        f = read(),t = read();
        add(f,t,1);
        add(t,f,1);
    }
    dfs(K,0);
    work(K);
    printf("%d",ans);
    return 0;
}

T3