点分治题集
淀粉质
1.Codeforces 161D🔗
计算树上距离为\(k\)的点对数量,模板题
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 5e4+7;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
int n,k,root,sz[MAXN],maxsz[MAXN],dist[MAXN];
bool vis[MAXN];
LL ret;
vector<int> G[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int v : G[u]){
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void rua(int u, int f, int d, int &cnt){
dist[cnt++] = d;
for(int v : G[u]){
if(v==f or vis[v]) continue;
rua(v,u,d+1,cnt);
}
}
void calculate(int u, int d, int tag){
int cnt = 0;
rua(u,0,d,cnt);
sort(dist,dist+cnt);
int l = 0, r = cnt - 1;
while(l<r){
while(l<r and dist[l]+dist[r]>k) r--;
if(l==r) break;
ret += tag*(upper_bound(dist+l+1,dist+r+1,k-dist[l]) - lower_bound(dist+l+1,dist+r+1,k-dist[l]));
l++;
}
}
void divide(int u){
vis[u] = true;
calculate(u,0,1);
for(int v : G[u]){
if(vis[v]) continue;
calculate(v,1,-1);
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
int main(){
____();
cin >> n >> k;
for(int i = 1; i < n; i++){
int u, v; cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
cout << ret << endl;
return 0;
}
2.洛谷P4178 Tree🔗
求树上距离小于\(k\)的路径数量
类似模板,改一下统计时的操作即可
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 4e4+7;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
int n,sz[MAXN],maxsz[MAXN],root,dist[MAXN],k;
LL ret;
bool vis[MAXN];
vector<pair<int,int> > G[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(auto e : G[u]){
int v = e.first;
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void rua(int u, int f, int d, int &cnt){
dist[cnt++] = d;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(v==f or vis[v]) continue;
rua(v,u,d+w,cnt);
}
}
void calculate(int u, int d, int tag){
int cnt = 0;
rua(u,0,d,cnt);
sort(dist,dist+cnt);
int l = 0, r = cnt - 1;
while(l<r){
while(l<r and dist[l]+dist[r]>k) r--;
if(l==r) break;
ret += tag * (r-l);
l++;
}
}
void divide(int u){
vis[u] = true;
calculate(u,0,1);
for(auto e : G[u]){
int v = e.first, w = e.second;
if(vis[v]) continue;
calculate(v,w,-1);
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
int main(){
____();
cin >> n;
for(int i = 1; i < n; i++){
int u, v, w;
cin >> u >> v >> w;
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
cin >> k;
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
cout << ret << endl;
return 0;
}
3.洛谷P4149[IOI2011]Race🔗
存一个\(dist[]\)数组,对于每次分治的根,\(dist[i]\)表示到当前根路径边权和为\(i\)的最小边数,遍历每棵子树,计算两个距离\(d1,d2\),\(d1\)是带边权距离,\(d2\)是边的数量,每次到一个点之后,可以更新答案为\(ret = min(ret,dist[k-d1]+d2)\)
然后更新整棵子树,所有子树遍历完之后,再遍历一遍清空数组
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e5+7;
const int MAXK = 1e6+7;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
int n,k,ret,sz[MAXN],maxsz[MAXN],root,dist[MAXK];
bool vis[MAXN];
vector<pair<int,int> > G[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(auto e : G[u]){
int v = e.first;
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void calculate(int u, int f, int d1, int d2){
if(d1>k) return;
ret = min(ret,dist[k-d1]+d2);
for(auto e : G[u]){
int v = e.first, w = e.second;
if(v==f or vis[v]) continue;
calculate(v,u,d1+w,d2+1);
}
}
void update(int u, int f, int d1, int d2, int tag){
if(d1>k) return;
if(tag==1) dist[d1] = min(dist[d1],d2);
else dist[d1] = INF;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(v==f or vis[v]) continue;
update(v,u,d1+w,d2+1,tag);
}
}
void divide(int u){
vis[u] = true;
dist[0] = 0;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(vis[v]) continue;
calculate(v,0,w,1);
update(v,0,w,1,1);
}
update(u,0,0,0,-1);
for(auto e : G[u]){
int v = e.first;
if(vis[v]) continue;
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
int main(){
____();
cin >> n >> k;
for(int i = 1; i < n; i++){
int u, v, w;
cin >> u >> v >> w;
u++; v++;
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
memset(dist,0x3f,sizeof(dist));
ret = INF;
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
cout << (ret==INF?-1:ret) << endl;
return 0;
}
4.洛谷P2634[国家集训队]聪聪可可🔗
开个数组统计一下模\(3\)余数的个数就好了
注意同一条路径\(u,v\)和\(v,u\)是不同的,一个点也算一条路径
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 2e4+7;
const int INF = 0x3f3f3f3f;
typedef long long int LL;
LL ret;
int n,root,sz[MAXN],maxsz[MAXN],dist[3];
bool vis[MAXN];
vector<pair<int,int> > G[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(auto e : G[u]){
int v = e.first;
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void calculate(int u, int f, int d){
ret += dist[(3-d)%3];
for(auto e : G[u]){
int v = e.first, w = e.second;
if(v==f or vis[v]) continue;
calculate(v,u,(d+w)%3);
}
}
void update(int u, int f, int d, int tag){
dist[d] += tag;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(v==f or vis[v]) continue;
update(v,u,(d+w)%3,tag);
}
}
void divide(int u){
vis[u] = true;
dist[0] = 1;
for(auto e : G[u]){
int v = e.first, w = e.second;
if(vis[v]) continue;
calculate(v,0,w);
update(v,0,w,1);
}
update(u,0,0,-1);
for(auto e : G[u]){
int v = e.first;
if(vis[v]) continue;
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
int main(){
____();
cin >> n;
for(int i = 1; i < n; i++){
int u, v, w;
cin >> u >> v >> w;
w %= 3;
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
LL num = ret * 2 + n, den = 1ll * n * n;
LL g = __gcd(num,den);
num /= g; den /= g;
cout << num << '/' << den << endl;
return 0;
}
5.洛谷P2664树上游戏🔗
神仙题 转为计算每条链答案的贡献
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 1e5+7;
const int INF = 0x3f3f3f3f;
int n,root,sz[MAXN],maxsz[MAXN],cnt[MAXN],c[MAXN];
LL sum,color[MAXN],ret[MAXN];
bool vis[MAXN];
vector<int> G[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int v : G[u]){
if(vis[v] or v==f) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void calculate_cbt(int u, int f){
sz[u] = 1;
cnt[c[u]]++;
for(int v : G[u]){
if(v==f or vis[v]) continue;
calculate_cbt(v,u);
sz[u] += sz[v];
}
if(cnt[c[u]]==1){
color[c[u]] += sz[u];
sum += sz[u];
}
cnt[c[u]]--;
}
void update(int u, int f, int tag){
cnt[c[u]]++;
for(int v : G[u]){
if(v==f or vis[v]) continue;
update(v,u,tag);
}
if(cnt[c[u]]==1){
color[c[u]] += sz[u] * tag;
sum += sz[u] * tag;
}
cnt[c[u]]--;
}
void rua(int u, int f, int dif, LL rhssz){
cnt[c[u]]++;
if(cnt[c[u]]==1){
dif++;
sum -= color[c[u]];
}
ret[u] += sum + rhssz * dif;
for(int v : G[u]){
if(v==f or vis[v]) continue;
rua(v,u,dif,rhssz);
}
if(cnt[c[u]]==1) sum += color[c[u]];
cnt[c[u]]--;
}
void clear(int u, int f){
cnt[c[u]] = color[c[u]] = 0;
for(int v : G[u]){
if(v==f or vis[v]) continue;
clear(v,u);
}
}
void solve(int u){
calculate_cbt(u,0);
ret[u] += sum; // 对根节点的贡献就是所有颜色的贡献和
for(int v : G[u]){
if(vis[v]) continue;
LL rhstreesz = sz[u] - sz[v];
cnt[c[u]]++;
color[c[u]] -= sz[v];
sum -= sz[v];
update(v,u,-1);
cnt[c[u]]--;
rua(v,u,0,rhstreesz);
cnt[c[u]]++;
color[c[u]] += sz[v];
sum += sz[v];
update(v,u,1);
cnt[c[u]]--;
}
clear(u,0);
sum = 0;
}
void divide(int u){
solve(u);
vis[u] = true;
for(int v : G[u]){
if(vis[v]) continue;
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
int main(){
scanf("%d",&n);
for(int i = 1; i <= n; i++) scanf("%d",&c[i]);
for(int i = 1; i < n; i++){
int u, v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
for(int i = 1; i <= n; i++) printf("%lld\n",ret[i]);
return 0;
}
6.洛谷P4292[WC2010]重建计划🔗
首先题目所给的式子是个分数规划\(k = \frac{\sum_{e\in S}v(e)}{|S|}\)
我们枚举这个二分值\(k\),可以得到\(\sum_{e\in S}v(e)-k|S|=\sum_{e\in S}(v(e)-k)\)
我们希望这个值取到最大值,即\(h(k)=max\{\sum_{e\in S}(v(e)-k)\}\)
假设答案应该是\(k*\),有三种情况:
\(\begin{cases} h(k)=0 & k=k* \\ h(k)<0 & k>k* \\ h(k)>0 & k>k* \end{cases}\)
接下来就是找最大的\(h(k)\),可以用点分治来做,每一层遍历所有子树,找到匹配的在区间内的最小值,如果用线段树来做的话要\(n\log ^3 n\),复杂度太大,仔细考虑可以发现其实可以用单调队列来做
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
const int INF = 0x3f3f3f3f;
const double eps = 3e-4;
int n,L,R,root,sz[MAXN],maxsz[MAXN],maxdep[MAXN],depth[MAXN];
bool vis[MAXN];
double dist[MAXN],d[MAXN];
vector<pair<int,double> > G[MAXN];
vector<int> Gt,vec[MAXN];
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(auto &e : G[u]){
int v = e.first;
if(vis[v] or v==f) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void divide(int u){
vis[u] = true;
Gt.push_back(u);
for(auto &e : G[u]){
int v = e.first;
if(vis[v]) continue;
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
void getTree(){
root = 0; maxsz[0] = INF;
findroot(1,0,n);
divide(root);
}
void bfs(int rt, double w){
depth[rt] = 1; //离根的深度
d[rt] = w; //离根的距离
queue<int> que;
que.push(rt);
vis[rt] = true;
while(!que.empty()){
int u = que.front();
que.pop();
vec[rt].push_back(u);
for(auto &e : G[u]){
int v = e.first;
if(vis[v]) continue;
d[v] = d[u] + e.second;
depth[v] = depth[u] + 1;
que.push(v);
vis[v] = true;
}
}
for(int node : vec[rt]) vis[node] = false; //复原暂时使用的vis数组
maxdep[rt] = depth[vec[rt].back()]; //子树中深度最大值
}
bool check(double m){
fill(begin(vis),end(vis),false);
fill(begin(dist),end(dist),-1e9);
for(int i = 1; i <= n; i++) for(auto &e : G[i]) e.second -= m;
for(int rt : Gt){
vis[rt] = true;
vector<int> ord; //记录子树的遍历顺序
for(auto &e : G[rt]){
int v = e.first;
if(vis[v]) continue;
bfs(v,e.second); //按节点深度遍历
ord.push_back(v);
}
// 按子树最大深度排序
sort(ord.begin(),ord.end(),[](const int &lhs, const int &rhs){
return maxdep[lhs] < maxdep[rhs];
});
int top = 0; // 当前遍历的所有子树的最大深度
dist[0] = 0; // 根节点的最短距离是0
for(int u : ord){ // 按子树最大深度从小到大遍历所有子树
deque<int> dq; // 单调队列
int now = top; // 深度指针
for(int x : vec[u]){ // u子树中节点深度从浅到深
int dep = depth[x];
double dt = d[x];
if(dep>R) break; // 已经超过了右边界 之后的必然都不可能了,直接break
if(dep+top<L) continue; // 最小都达不到,continue到深度更深的
int lt = max(L-dep,0); // 当前其他子树可到达的深度左边界
while(now>=lt){
while(!dq.empty() and dist[dq.back()]<=dist[now]) dq.pop_back(); // 单调递减的单调队列
dq.push_back(now);
now--;
}
while(!dq.empty() and dq.front()+dep>R) dq.pop_front(); // 超过右边界的点
if(dist[dq.front()]+dt>=0){ // 找到了一条符合条件的链
for(int i = 1; i <= n; i++) for(auto &e : G[i]) e.second += m; // 恢复边权
for(int u : ord) vec[u].clear();
return true;
}
}
for(int x : vec[u]){ // 把当前的子树加入到使用过的子树中
if(dist[depth[x]]==-1e9) top = depth[x]; // 之前没有出现过的深度, 更新最深深度
dist[depth[x]] = max(dist[depth[x]],d[x]); // 相同深度取最大值
}
}
for(int u : ord) for(int v : vec[u]) dist[depth[v]] = -1e9; //一层遍历完 恢复
for(int u : ord) vec[u].clear(); // 同上
}
for(int i = 1; i <= n; i++) for(auto &e : G[i]) e.second += m; // 恢复边权
return false;
}
int main(){
scanf("%d %d %d",&n,&L,&R);
for(int i = 1; i < n; i++){
int u, v, w;
scanf("%d %d %d",&u,&v,&w);
G[u].push_back(make_pair(v,w));
G[v].push_back(make_pair(u,w));
}
getTree(); //得到dfs序下的点分树
double l = 0, r = 1e6;
while(r-l>eps){ //分数规划 二分最优解
double mid = (l + r) / 2;
if(check(mid)) l = mid;
else r = mid;
}
printf("%.3f\n",(l+r)/2);
return 0;
}
7.HDU4812 D Tree🔗
先线性预处理逆元
用数组存每个出现过的乘积
然后点分治处理,每一层分别处理子树,先对每一棵子树查询答案,再更新数组
假设根节点到当前节点的乘积为\(val\),查询答案就是找数组中是否存在\(inv[val]*k%MOD\)
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 1e5+7;
const int MAXM = 1e6+7;
const int MOD = 1e6+3;
const int INF = 0x3f3f3f3f;
int n,k,root,sz[MAXN],maxsz[MAXN],mt[MAXM];
LL inv[MAXM],w[MAXN];
bool vis[MAXN];
vector<int> G[MAXN];
pair<int,int> ret;
void preprocess(){
inv[1] = 1;
for(int i = 2; i < MAXM; i++) inv[i] = (MOD-MOD/i) * inv[MOD%i] % MOD;
}
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int v : G[u]){
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[root]>maxsz[u]) root = u;
}
void calculate(int u, int f, int val){
val = 1ll * val * w[u] % MOD;
if(mt[inv[val]*k%MOD]!=INF)
ret = min(ret,make_pair(min(u,mt[inv[val]*k%MOD]),max(u,mt[inv[val]*k%MOD])));
for(int v : G[u]){
if(v==f or vis[v]) continue;
calculate(v,u,val);
}
}
void update(int u, int f, int val){
val = 1ll * val * w[u] % MOD;
mt[val] = min(mt[val],u);
for(int v : G[u]){
if(v==f or vis[v]) continue;
update(v,u,val);
}
}
void clear(int u, int f, int val){
val = 1ll * val * w[u] % MOD;
mt[val] = INF;
for(int v : G[u]){
if(v==f or vis[v]) continue;
clear(v,u,val);
}
}
void divide(int u){
vis[u] = true;
mt[w[u]] = u;
for(int v : G[u]){
if(vis[v]) continue;
calculate(v,u,1);
update(v,u,w[u]);
}
clear(u,0,1);
for(int v : G[u]){
if(vis[v]) continue;
root = 0; findroot(v,0,sz[v]);
divide(root);
}
}
void solve(){
for(int i = 1; i <= n; i++) scanf("%I64d",&w[i]);
for(int i = 1; i <= n; i++) G[i].clear();
memset(vis,0,sizeof(vis));
memset(mt,0x3f,sizeof(mt));
ret = make_pair(INF,INF);
for(int i = 1; i < n; i++){
int u, v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
maxsz[0] = INF;
root = 0; findroot(1,0,n);
divide(root);
if(ret.first==INF) puts("No solution");
else printf("%d %d\n",ret.first,ret.second);
}
int main(){
preprocess();
while(scanf("%d %d",&n,&k)!=EOF) solve();
return 0;
}
8.HDU5977 Garden of Eden 🔗
点分治+SOSDP
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
typedef long long int LL;
const int MAXN = 5e4+7;
const int N = 10;
LL ret;
int n,k,f[1<<N],g[1<<N],root,sz[MAXN],maxsz[MAXN],col[MAXN];
bool vis[MAXN];
vector<int> G[MAXN];
void findroot(int u, int fa, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int v : G[u]){
if(v==fa or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void search(int u, int fa, int msk){
f[msk|=col[u]]++;
for(int v : G[u]){
if(v==fa or vis[v]) continue;
search(v,u,msk);
}
}
void calculate(int u, int msk, LL tag){
for(int i = 0; i < (1<<k); i++) f[i] = 0;
search(u,0,msk);
for(int i = 0; i < (1<<k); i++) g[i] = f[i];
for(int bit = 0; bit < k; bit++) for(int msk = 0; msk < (1<<k); msk++) if(!(msk&(1<<bit))) g[msk] += g[msk^(1<<bit)];
for(int msk = 0; msk < (1<<k); msk++) ret += tag * f[msk] * g[((1<<k)-1)^msk];
}
void divide(int u){
vis[u] = true;
calculate(u,0,1);
for(int v : G[u]){
if(vis[v]) continue;
calculate(v,col[u],-1);
root = 0; findroot(v,u,sz[v]);
divide(root);
}
}
void solve(){
for(int i = 1; i <= n; i++) G[i].clear();
memset(vis,0,sizeof(vis));
for(int i = 1; i <= n; i++) scanf("%d",&col[i]);
for(int i = 1; i <= n; i++) col[i] = (1<<(col[i]-1));
for(int i = 1; i < n; i++){
int u, v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
G[v].push_back(u);
}
ret = 0;
maxsz[0] = MAXN;
root = 0; findroot(1,0,n);
divide(root);
printf("%I64d\n",ret);
}
int main(){
while(scanf("%d %d",&n,&k)!=EOF) solve();
return 0;
}
动态淀粉质
1. 洛谷P6329【模板】点分树 | 震波🔗
点分树上维护线段树,每次往点分树的父亲上遍历修改
查询的时候线段树加容斥原理
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
int n,m,tp[MAXN],val[MAXN],root,sz[MAXN],maxsz[MAXN],par[MAXN][18],depth[MAXN],fa[MAXN];
bool vis[MAXN];
struct Graph{
int head[MAXN],to[MAXN<<1],nxt[MAXN<<1],tot;
void ADDEDGE(int u, int v){
++tot;
to[tot] = v;
nxt[tot] = head[u];
head[u] = tot;
}
}G;
struct SegmentTree{
int pt[MAXN],tot,sum[MAXN*25],ls[MAXN*25],rs[MAXN*25];
void update(int pos, int x, int &rt, int L, int R){
if(!rt) rt = ++tot;
sum[rt] += x;
if(L+1==R) return;
int mid = (L + R) >> 1;
if(pos<mid) update(pos,x,ls[rt],L,mid);
else update(pos,x,rs[rt],mid,R);
}
int query(int l, int r, int rt, int L, int R){
if(l>=R or L>=r) return 0;
if(l<=L and R<=r) return sum[rt];
int mid = (L + R) >> 1;
return query(l,r,ls[rt],L,mid) + query(l,r,rs[rt],mid,R);
}
}ST[2];
void dfs(int u, int f){
par[u][0] = f;
depth[u] = depth[f] + 1;
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(v==f) continue;
dfs(v,u);
}
}
int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; depth[u] - depth[v]; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 17; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
inline int dist(int u, int v){ return depth[u] + depth[v] - 2 * depth[LCA(u,v)]; }
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void divide(int u, int f){
vis[u] = true;
fa[u] = f;
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(vis[v]) continue;
root = 0; findroot(v,u,sz[v]);
maxsz[root] = sz[v];
divide(root,u);
}
}
void modify(int u, int w){
int rt = u;
while(rt){
ST[0].update(dist(u,rt),w,ST[0].pt[rt],0,maxsz[rt]);
if(fa[rt]) ST[1].update(dist(u,fa[rt]),w,ST[1].pt[rt],0,maxsz[fa[rt]]);
rt = fa[rt];
}
val[u] += w;
}
int query(int u, int k){
int ret = 0, rt = u;
while(rt){
if(k-dist(rt,u)>=0) ret += ST[0].query(0,k-dist(rt,u)+1,ST[0].pt[rt],0,maxsz[rt]);
if(fa[rt] and k-dist(fa[rt],u)) ret -= ST[1].query(0,k-dist(fa[rt],u)+1,ST[1].pt[rt],0,maxsz[fa[rt]]);
rt = fa[rt];
}
return ret;
}
int main(){
scanf("%d %d",&n,&m);
for(int i = 1; i <= n; i++) scanf("%d",&tp[i]);
for(int i = 1; i < n; i++){
int u, v;
scanf("%d %d",&u,&v);
G.ADDEDGE(u,v); G.ADDEDGE(v,u);
}
dfs(1,0);
maxsz[0] = MAXN;
root = 0; findroot(1,0,n);
divide(root,0);
for(int i = 1; i <= n; i++) modify(i,tp[i]);
int lastans = 0;
while(m--){
int op; scanf("%d",&op);
if(!op){
int x, k; scanf("%d %d",&x,&k);
x ^= lastans; k ^= lastans;
printf("%d\n",lastans=query(x,k));
}
else{
int x, y; scanf("%d %d",&x,&y);
x ^= lastans; y ^= lastans;
modify(x,y-val[x]);
}
}
return 0;
}
2.洛谷P2056 [ZJOI2007]捉迷藏🔗
动态淀粉质+堆
view code
//#pragma GCC optimize("O3")
//#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<bits/stdc++.h>
using namespace std;
function<void(void)> ____ = [](){ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);};
const int MAXN = 1e5+7;
int n,q,root,sz[MAXN],maxsz[MAXN],fa[MAXN],par[MAXN][20],depth[MAXN],state[MAXN],num;
bool vis[MAXN];
struct Graph{
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN],tot;
void ADDEDGE(int u, int v){
tot++;
to[tot] = v; nxt[tot] = head[u];
head[u] = tot;
}
}G;
class Priorityqueue{
private:
priority_queue<int> q1,q2;
void clr(){ while(!q2.empty() and q1.top()==q2.top()) q1.pop(), q2.pop(); }
public:
bool empty(){ return q1.empty(); }
void push(int x){ q1.push(x); }
void pop(){ q1.pop(); clr(); }
int top(){ return q1.top(); }
void erase(int x){ q2.push(x); clr(); }
int size(){ return q1.size() - q2.size(); }
int sectop(){ int x = top(); pop(); int y = top(); push(x); return y; }
}QA[MAXN],QB[MAXN],Q;
void dfs(int u, int f){
depth[u] = depth[par[u][0]=f] + 1;
for(int i = 1; par[u][i-1]; i++) par[u][i] = par[par[u][i-1]][i-1];
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(v==f) continue;
dfs(v,u);
}
}
inline int LCA(int u, int v){
if(depth[u]<depth[v]) swap(u,v);
for(int i = 0; depth[u]-depth[v]; i++) if((depth[u]-depth[v])&(1<<i)) u = par[u][i];
if(u==v) return u;
for(int i = 19; i >= 0; i--) if(par[u][i]!=par[v][i]){
u = par[u][i];
v = par[v][i];
}
return par[u][0];
}
inline int dist(int u, int v){ return depth[u] + depth[v] - 2 * depth[LCA(u,v)]; }
void findroot(int u, int f, int tot){
sz[u] = 1; maxsz[u] = 0;
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(v==f or vis[v]) continue;
findroot(v,u,tot);
sz[u] += sz[v];
maxsz[u] = max(maxsz[u],sz[v]);
}
maxsz[u] = max(maxsz[u],tot-sz[u]);
if(maxsz[u]<maxsz[root]) root = u;
}
void divide(int u, int f){
fa[u] = f;
vis[u] = true;
for(int i = G.head[u]; i; i = G.nxt[i]){
int v = G.to[i];
if(vis[v]) continue;
root = 0; findroot(v,root,sz[v]);
divide(root,u);
}
}
void update(int x, int tag){
if(tag==1 and QB[x].size()>=2) Q.push(QB[x].top() + QB[x].sectop());
if(tag==-1 and QB[x].size()>=2) Q.erase(QB[x].top() + QB[x].sectop());
}
void turn_off(int u){
num++; state[u] = 0;
update(u,-1);
QB[u].push(0);
update(u,1);
for(int rt = u; fa[rt]; rt = fa[rt]){
int d = dist(fa[rt],u);
update(fa[rt],-1);
if(!QA[rt].empty()) QB[fa[rt]].erase(QA[rt].top());
QA[rt].push(d);
QB[fa[rt]].push(QA[rt].top());
update(fa[rt],1);
}
}
void turn_on(int u){
num--; state[u] = 1;
update(u,-1);
QB[u].erase(0);
update(u,1);
for(int rt = u; fa[rt]; rt = fa[rt]){
int d = dist(fa[rt],u);
update(fa[rt],-1);
QB[fa[rt]].erase(QA[rt].top());
QA[rt].erase(d);
if(!QA[rt].empty()) QB[fa[rt]].push(QA[rt].top());
update(fa[rt],1);
}
}
inline int read(){
int x = 0, f = 1;
char c = getchar();
while(c!='-'&&(c<'0'||c>'9')) c = getchar();
if(c=='-') f = -1,c = getchar();
while(c>='0'&&c<='9') x = x*10+c-'0', c = getchar();
return f*x;
}
int main(){
n = read();
for(int i = 1; i < n; i++){
int u = read(), v = read();
G.ADDEDGE(u,v); G.ADDEDGE(v,u);
}
dfs(1,0);
maxsz[0] = MAXN;
root = 0; findroot(1,0,n);
divide(root,0);
for(int i = 1; i <= n; i++) turn_off(i);
q = read();
while(q--){
char op[2];
scanf("%s",op);
if(op[0]=='G'){
if(num==0) printf("-1\n");
else if(num==1) printf("0\n");
else printf("%d\n",Q.top());
}
else{
int x = read();
if(state[x]) turn_off(x);
else turn_on(x);
}
}
return 0;
}