稳操胜券 Solution Code

  有一个自变量为可重集合$S$的函数$F(S)$,其值为不能被$S$的某个子集之和表示出来的最小正整数

  给定一棵树,每个点有一个点权。每次询问一条路径$(x,y)$,参数为$k$,令$S$为$(x,y)$路径上的所有点权外加$k$个你任意指定的正整数构成的集合,请最大化$F(S)$

  

Solution

  先考虑$k=0$怎么做,尝试寻找一个高效的判定方法。

  我们将集合内元素排序后从小到大来考虑:记已考虑元素构成集合为$A$,假设$A$能凑出的值域是连续的一段$[1,F(A))$,考虑下一个加入的元素$a$:

  • 若$a > F(A)$:值域将出现不可补救的空隙,即答案已经固定,可以直接退出
  • 若$a le F(A)$:$S$能凑出的值域将更新为$[1,F(S)+a)$

  形式化并加速这个过程,我们可以得出一个算法:

  1. 初始时$A=emptyset$,$F(A)=1$
  2. 求出不超过$F(A)$的元素之和,记为$sum$
  3. 若$sum ge F(A)$,令$F(A)=sum+1$,转2.
  4. 若$sum < F(A)$,答案即为$F(A)$,退出

  3.相当于把一堆满足$a le F(A)$的$a$加入了$A$中。加入只会导致$F(A)$增大,所以它们可以批量处理

  4.对应着无合法$a$的情况。事实上,$sum$此时一定等于$F(A)-1$

  $F(A)$本质上就是$A$中元素之和加一,只不过新元素加入$A$时必须满足当前$F(A)$的限制

  若$k > 0$,则相当于在4.处给了若干次挽救的机会:我们可以往集合中贪心地添加一个$F(A)$,这样就可以使值域继续扩展了

  注意到需要模拟的步骤只有$log$次,剩余的$k$只需要对答案不断乘2即可,用double乱搞

  

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149

#include <algorithm>
using namespace std;
typedef long long LL;
const int N=100000+10;
const int B=17;
int n;
int a[N];
int dcnt;
LL d[N];
namespace Seg{
  const int SIZE=N*20;
  int rt[N];
  int nodeCnt;
  int ch[SIZE][2];
  LL sum[SIZE];
  int (int u){
    int v=++nodeCnt;
    ch[v][0]=ch[u][0];
    ch[v][1]=ch[u][1];
    sum[v]=sum[u];
    return v;
  }
  void insert(int u,int &v,int l,int r,int x){
    v=copyNode(u);
    sum[v]+=d[x];
    if(l==r)
      return;
    int mid=(l+r)>>1;
    if(x<=mid)
      insert(ch[u][0],ch[v][0],l,mid,x);
    else
      insert(ch[u][1],ch[v][1],mid+1,r,x);
  }
  LL query(int u,int l,int r,int x){
    if(u==0)
      return 0;
    if(l==r)
      return sum[u];
    int mid=(l+r)>>1;
    if(x<=mid)
      return query(ch[u][0],l,mid,x);
    else
      return sum[ch[u][0]]+query(ch[u][1],mid+1,r,x);
  }
}
namespace Tree{
  int h[N];
  struct Edge{
    int v,next;
  }e[N*2];
  int etot;
  int dep[N];
  int pre[N][B+1];
  void addEdge(int u,int v){
    e[++etot]=(Edge){v,h[u]}; h[u]=etot;
    e[++etot]=(Edge){u,h[v]}; h[v]=etot;
  }
  void buildDFS(int u,int fa){
    dep[u]=dep[fa]+1;
    pre[u][0]=fa;
    for(int i=1;i<=B;i++) pre[u][i]=pre[pre[u][i-1]][i-1];
    Seg::insert(Seg::rt[fa],Seg::rt[u],1,dcnt,a[u]);
    for(int i=h[u],v;i;i=e[i].next)
      if((v=e[i].v)!=fa)
        buildDFS(v,u);
  }
  int getLCA(int a,int b){
    if(dep[a]<=dep[b])
      swap(a,b);
    for(int i=B;i>=0;i--)
      if(dep[pre[a][i]]>=dep[b])
        a=pre[a][i];
    if(a==b)
      return a;
    for(int i=B;i>=0;i--)
      if(pre[a][i]!=pre[b][i]){
        a=pre[a][i];
        b=pre[b][i];
      }
    return pre[a][0];
  }
  LL queryPath(int u,int v,int lca,int x){
    return Seg::query(Seg::rt[u],1,dcnt,x)+
       Seg::query(Seg::rt[v],1,dcnt,x)-
       Seg::query(Seg::rt[lca],1,dcnt,x)*2+
       (a[lca]<=x?d[a[lca]]:0);
  }
}
void readData(){
  scanf("%d",&n);
  for(int i=1;i<n;i++){
    int u,v;
    scanf("%d%d",&u,&v);
    Tree::addEdge(u,v);
  }
  for(int i=1;i<=n;i++) scanf("%d",&a[i]);
}
void Diz(){
  for(int i=1;i<=n;i++) d[++dcnt]=a[i];
  d[++dcnt]=0;
  sort(d+1,d+dcnt+1);
  dcnt=unique(d+1,d+dcnt+1)-d-1;
  for(int i=1;i<=n;i++) a[i]=lower_bound(d+1,d+dcnt+1,a[i])-d;
}
int DizFind(LL x){
  int p=upper_bound(d+1,d+dcnt+1,x)-d;
  return p-1;
}
double solve(int a,int b,int k){
  int lca=Tree::getLCA(a,b);
  LL sum=Tree::queryPath(a,b,lca,dcnt);
  LL x=0,s,addition=0;
  while(true){
    LL origin=Tree::queryPath(a,b,lca,DizFind(x+1));
    s=origin+addition;
    if(s>x){
      x=s;
    }else if(k>0){
      if(origin==sum)
        break;
      k--;
      addition+=(x+1);
      x=s+(x+1);
    }else{
      break;
    }
  }
  double ans=x+1;
  while(k--)
    ans*=2;
  return ans;
}
void answerQuery(){
  int q;
  int x,y,k;
  scanf("%d",&q);
  while(q--){
    scanf("%d%d%d",&x,&y,&k);
    printf("%.0fn",solve(x,y,k));
  }
}
int main(){
  readData();
  Diz();
  Tree::buildDFS(1,0);
  answerQuery();
  return 0;
}