給定一棵
n 個節點的無根樹,每個點都有乙個非負整數的權值va
li,定義一條路徑的價值為路徑上的點權和減去路徑的點權最大值。
給定引數
p ,請求出樹上有多少條價值是
p的倍數的路徑。
注意:單點也算路徑。並且路徑(u
,v) 和(v
,u) 只算一次。1≤
n≤105
,1≤p
≤107,
0≤va
li≤10
9 很直觀的想法是點剖。
考慮對於乙個分治重心,我依次處理其各個子樹。對於一條重心到乙個點的路徑,假設其權值和是su
m2,最大值是mx
2 ,在以前遍歷過的子樹中,如果有一條權值和是su
m1,最大值是mx
1 的路徑滿足條件,當且僅當一下兩個條件其一成立: ∙m
x1≤m
x2,且su
m1+s
um2≡
mx2(
modp)
∙mx1
>mx
2 ,且su
m1+s
um2≡
mx1(
modp)
把後面的式子瞎jb化一下,變成只和
i 有關的形式,就可以開很多棵一棵動態開點的權值線段樹來查詢滿足前面條件的點數了。
時間複雜度o(
nlog2n
),常數有點大。
還存在某排序加刪掉重複部分的演算法,時間複雜度相同,但是常數小很多。
#include
#include
#include
using namespace std;
typedef long long ll;
intread()
const int n=100050;
const int e=n<<1;
const int p=10000000;
const int v=1000000000;
const int lgv=31;
const int s=n*lgv;
struct segment_tree
int newnode()
void insert(int &rt,int
x,int l,int r)
int query(int rt,int st,int en,int l,int r)
}t[2];
intlast[n],fa[n],que[n],size[n],val[n];
int tmp[n][2],temp[n][2];
int tov[e],nxt[e];
int root[p][2];
bool vis[n];
int n,p,head,tail,tot,tmps,temps;
ll ans;
void insert(int
x,int
y)int core(int og)
return ret;
}void dfs(int
x,int sum,int mx)
void solve(int
x) }
for (int sum,mx;temps;--temps)
vis[c]=1;
for (int i=last[c],y;i;i=nxt[i])
if (!vis[y=tov[i]]) solve(y);
}int main()
5055 樹上路徑
給定一顆 n 個結點的無根樹,每個點有乙個點權,定義一條路徑的價值為路徑上的點權和 路徑的點權最大值。給定引數 p,求有多少不同的樹上簡單路徑,滿足它的價值恰好是 p 的倍數。data constraint n 105,p 107 考慮點分治。對於當前的分治重心,把所有以它為起點的路徑取出來,按照路...
JZOJ4715樹上路徑 點分治
這居然是聯賽題我一臉懵逼。根本沒往那方向上想。題目大意 讓你求樹上最短的路徑,但是路徑長度必須 l,r,n 10 5,r l 10 6,l,r 10 9 其實是我點分治沒怎麼打過,這其實是一道很一眼的點分治的題。我是懵比了。可以算是模板題吧,我是直接點分治莽一波,然後就過了,題解還說是二分,我比賽時...
JZOJ 4715 樹上路徑 (點分)
description 給出一棵樹,求出最小的k,使得,且在樹中存在路徑p,使得k s且k e。k為路徑p上的邊的權值和 input 第一行給出n,s,e。n代表樹的點數,s,e如題目描述。下面n 1行給出這棵樹的相鄰兩個節點的邊及其權值w。output 輸出共一行乙個整數,表示答案。若無解輸出 1...