Analysis
点分治
1、求树的重心
2、计算以当前重心为根的子树的答案
3、去掉以当前重心儿子为根的子树的答案
4、枚举每个儿子,分治
考虑计算过程如何实现
我们不妨记一个cnt数组,cnt[i]表示使用i条边权值为k的有多少对
每次实现2的时候,权值设为+1
每次实现3的时候,权值设为-1
把子树内所有的dis排序,计算有多少对权值和为k的,两个指针扫一遍就可以了。
Code
#include<bits/stdc++.h>
#define in read()
#define re register
using namespace std;
inline int read(){
char ch;int f=1,res=0;
while((ch=getchar())<'0'||ch>'9') if(ch=='-') f=-1;
while(ch>='0'&&ch<='9'){
res=(res<<1)+(res<<3)+(ch^48);
ch=getchar();
}
return f==1?res:-res;
}
const int N=2e5+10;
int n,K;
int nxt[N<<1],head[N],to[N<<1],w[N<<1],ecnt=0;
inline void add(int x,int y,int z){
nxt[++ecnt]=head[x];head[x]=ecnt;to[ecnt]=y;w[ecnt]=z;
}
int sze[N],son[N];
bool vis[N];
int G,minn;
int maxn,cnt[2000009],res=N,ans=0;//ans-->长度为k的点对个数,res-->最少边
struct node{ int dis,dep; }a[N];
inline bool cmp(const node &a,const node &b){
return a.dis<b.dis;
}
int d[N],dep[N],num;
inline void dfs(int u,int fu){
a[++num].dis=d[u];a[num].dep=dep[u];
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
d[v]=d[u]+w[e];dep[v]=dep[u]+1;
dfs(v,u);
}
}
inline int calc(int u,int val,int h,int flag){
num=0;int t=0;
d[u]=val;dep[u]=h;
dfs(u,0);
sort(a+1,a+num+1,cmp);
for (int i=1,j=num;i<=j;i++)
{
while (j>i && a[i].dis+a[j].dis>K) j--;
for (int p=j;a[i].dis+a[p].dis==K;p--) cnt[a[i].dep+a[p].dep]+=flag;
}
return t;
}
inline void dfssize(int u,int fu){
sze[u]=1;
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
dfssize(v,u);
sze[u]+=sze[v];
}
}
inline int getG(int u,int fu,int size){
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
if(sze[v]>size) return getG(v,u,size);
}
return u;
}
inline void solve(int u){
minn=n;ans=0;
dfssize(u,0);
G=getG(u,0,sze[u]/2);
vis[G]=1;
ans+=calc(G,0,0,1);
for(re int e=head[G];e;e=nxt[e]){
int v=to[e];
if(vis[v]) continue;
ans-=calc(v,w[e],1,-1);
solve(v);
}
}
int main(){
n=in;K=in;
for(re int i=1;i<n;++i){
int x=in,y=in,z=in;x++;y++;
add(x,y,z);add(y,x,z);
}
solve(1);
res=-1;
for(re int i=0;i<=n;++i) if(cnt[i]) {res=i;break; }
printf("%d",res);
return 0;
}
吐槽
1.自己以前求重心的方法被卡爆.
一开始是这样的
void dfssize(int u,int fu){
sze[u]=1;
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
dfssize(v,u);
sze[u]+=sze[v];
if(sze[v]>son[u]) son[u]=sze[v];
}
}
void getG(int rt,int u,int fu){
if(sze[rt]-sze[u]>son[u]) son[u]=sze[rt]-sze[u];
if(son[u]<minn) minn=son[u],G=u;
for(re int e=head[u];e;e=nxt[e]){
int v=to[e];
if(vis[v]||v==fu) continue;
getG(rt,v,u);
}
}
(如果有这样写的盆友,一定要改鸭!!!
虽然我到现在也不知道错在哪里(=@__@=))
正确的打开方式:
void dfsSize(int u, int fa) {
size[u] = 1;
for (int i = 0, v; i < (int)g[u].size(); i++)
if (!vis[v = g[u][i]] && v != fa) dfsSize(v, u), size[u] += size[v];
}
int getG(int u, int fa, int n) {
for (int i = 0, v; i < (int)g[u].size(); i++)
if (!vis[v = g[u][i]] && v != fa && size[v] > n) return getG(v, u, n);
return u;
}
补充
xehoth学长说点分治TLE的情况只有几种:
I 重心求错
[网上很多求重心的方法都可能死循环或者求错,但也可以水过一些题。所以,选好模板很重要/笑。]
II calc的时候写成了n2
2.用双指针扫描的时候漏了情况
一开始是这样写的
int l=1,r=num;
while(l<r){
if(a[l].dis+a[r].dis<=K){
if(a[l].dis+a[r].dis==K) {
t++;
int hh=a[l].dep+a[r].dep;
cnt[hh]+=flag;
}
l++;
}
else r--;
}
后来发现这种情况会漏很多(以前居然用这个A了题,(╥╯^╰╥)感觉被骗)
比如A区间的值都一样,B区间的值也一样
我们在
l
l
l的时候发现
r
r
r合法,就累计了一次
而后
l
l
l就直接加1 ,就漏掉了整个B区间剩下的部分
正确的打开方式:
for (int i=1,j=num;i<=j;i++)
{
while (j>i && a[i].dis+a[j].dis>K) j--;
for (int p=j;a[i].dis+a[p].dis==K;p--) cnt[a[i].dep+a[p].dep]+=flag;
}
3.统计的时候做复杂了,各种T
没有必要每次做完一颗子树就统计
最后统一计算一下即可
就这样………
一个下午没了
o(╥﹏╥)o