题目质量很高,第二题提供了一个很好的解决同一类问题的思路
把每个点的权值赋在它的到父亲的那条条边 本题的主要思想是利用二进制位变换的周期性进行倍增然后求解,十分精妙,明天再打一遍
这道题大意是求一棵树上u到v的路径上的所有点dis(u,w)|val[w]的和,利用二进制变换的周期性来进行倍增.
//把每个点的权值赋在它的到父亲的那条条边
// 本题的主要思想是利用二进制位变换的周期性进行倍增然后求解,十分精妙,明天再打一遍
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long LL;
const LL N = 500005 , M = N * 2 , Bi = 21;
LL n , m , ne[M] , fir[N] , to[M] , val[N] , st[N][Bi] , up[N][Bi] , dn[N][Bi] , u , v , x , y , cnt = 1 , depth[N] , Get[N][Bi] , fa[N];
#define Foreachson(i , x) for(LL i = fir[x];i;i = ne[i])
void link(LL x ,LL y) {
ne[++ cnt] = fir[x]; fir[x] = cnt; to[cnt] = y; ne[++ cnt] = fir[y]; fir[y] = cnt; to[cnt] = x;
}
void dfs(LL x , LL f) {
fa[x] = f; depth[x] = depth[f] + 1;
for(LL i = 0;i < Bi;i ++) Get[x][i] += Get[f][i] , Get[x][i] += ((val[x] & (1 << i)) == 0) * (1 << i);
st[x][0] = f; for(LL i = 1;i < Bi;i ++) st[x][i] = st[st[x][i - 1]][i - 1]; up[x][0] = val[x]; dn[x][0] = val[x];
for(LL i = 1;i < Bi;i ++) {
LL it = st[x][i - 1];
up[x][i] = up[x][i - 1] + up[it][i - 1] + Get[it][i - 1] - Get[st[x][i]][i - 1];
dn[x][i] = dn[x][i - 1] + dn[it][i - 1] + Get[x][i - 1] - Get[it][i - 1];
}
Foreachson(i , x) if(to[i] != f) dfs(to[i] , x);
}
LL LCA(LL x , LL y) {
if(depth[y] < depth[x]) swap(x , y);
for(LL i = Bi - 1;i >= 0;i --) if(depth[x] <= depth[st[y][i]]) y = st[y][i];
if(x == y) return x;
for(LL i = Bi - 1;i >= 0;i --) if(st[x][i] != st[y][i]) x = st[x][i] , y = st[y][i];
return st[x][0];
}
LL upcalc(LL x , LL l) {
LL res = 0; l = fa[l];
for(LL i = 20; ~i;i --) {
if(depth[st[x][i]] >= depth[l]) {
res += up[x][i];
x = st[x][i];
res += Get[x][i] - Get[l][i];
}
}
return res;
}
LL dncalc(LL x , LL d) {
LL res = 0; LL s = x; d++;
for(LL i = 0;i <= 20;i ++) {
if(d & (1 << i)) {
res += dn[x][i];
res += Get[s][i] - Get[x][i];
x = st[x][i];
}
}
return res;
}
LL calc(LL x , LL y) {
LL lca = LCA(x , y) , d = depth[x] + depth[y] - depth[lca] * 2;
return upcalc(x , lca) + dncalc(y , d) - dncalc(lca , depth[x] - depth[lca]);
}
main(void) {
scanf("%lld",&n);
for(LL i = 1;i <= n;i ++) scanf("%lld",&val[i]);
for(LL i = 1;i <= n - 1;i ++) scanf("%lld%lld",&x,&y) , link(x , y);
dfs(1, 0); LL t; scanf("%lld",&t);
for(LL i = 1;i <= t;i ++) {
scanf("%lld%lld",&u,&v);
printf("%lld\n" , calc(u,v));
}
}
第三题是一道KMP上套DP的题,以后遇到这种题利用next数组的性质可以大胆的猜想一发等价的结论.
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 500005; int dp[N] , ne[N] , match[N] , len; char s[N];
int main(void) {
scanf("%s",s + 1); len = strlen(s + 1); int j , i;puts("1");
for(i = 2,ne[0] = -1 , dp[1] = match[1] = 1, j = 0;i <= len;printf("%d\n",dp[i]) , i++) {
while(j != -1 && s[i] != s[j + 1]) j = ne[j]; ne[i] = ++j;
if(match[dp[ne[i]]] + ne[i] >= i) dp[i] = dp[ne[i]] , match[dp[i]] = i; else dp[i] = i , match[i] = i;
}
}