题目大意:

给定一棵树,每一条边有$L,R$两种权值,求有多少条路径满足$\max(L)\leq\min(R)$。

解法$1-$点分治$+$二维数点

统计树上的路径应首先想到点分治,我们很显然可以搜出过从分治重心出发的每一条路径,对应着当前重心的每一棵子树存在的若干个区间$[L_i,R_i]$,若两个不同的子树内的区间产生贡献,即这两个点形成的路径符合要求,当且仅当$[L_1,R_1]\cap[L_2,R_2]\ne\emptyset$,换言之,当两个点形成的路径没有贡献,当且仅当$R_1<L_2$或$R_2<L_1$,那么考虑先离散化,按顺序处理每一棵子树时,用树状数组维护已插入的点中$R$值和$L$值。这样,枚举到当子树时,用之前枚举的所子树中点的数量,减去$L$值过大和$R$值过小的数量,就得到了这个点对最终答案的影响。

每一条符合路径的路径仅会被路径上等级最高的分治重心所统计。如果最高等级的分治重心有两个,那么他们之间一定会有一个等级更高的分治重心。

最终复杂度为$O(N\log^2N)$,所以本机跑了$1.7s$考场上拿到了$91$分$hhh$。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200004
using namespace std;
int read(){
int nm=0,fh=1; char cw=getchar();
for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return nm*fh;
}
int n,m,c[M<<2][2],u[M],v[M],L[M<<1],R[M<<1],G[M],cnt;
int tmp,fs[M],nt[M<<1],to[M<<1],U[M<<1],D[M<<1];
int id[M<<1],od[M<<1],sum,rt,sz[M],mxs[M];
void add(int k,int x,int kd){for(;k<=m;k+=(k&-k)) c[k][kd]+=x;}
int qry(int k,int kd){int tt=0;for(;k;k-=(k&-k)) tt+=c[k][kd]; return tt;}
LL ans;
bool cmp(int x,int y){return (od[x]+od[y]==0)?od[x]<od[y]:abs(od[x])<abs(od[y]);}
bool vis[M];
void fdrt(int x,int last){
sz[x]=1,mxs[x]=0;
for(int i=fs[x];~i;i=nt[i]){
if(to[i]==last||vis[to[i]]) continue;
fdrt(to[i],x),sz[x]+=sz[to[i]];
mxs[x]=max(mxs[x],sz[to[i]]);
}
mxs[x]=max(mxs[x],sum-sz[x]);
if(mxs[x]<mxs[rt]) rt=x;
}
void dfs(int x,int last,int minn,int maxn){
if(minn>maxn) return; sz[x]=1;
ans+=cnt-qry(m-maxn,0)-qry(minn-1,1);
for(int i=fs[x];i!=-1;i=nt[i]){
if(vis[to[i]]||to[i]==last) continue;
dfs(to[i],x,max(minn,L[i]),min(maxn,R[i]));
sz[x]+=sz[to[i]];
}
}
void ins(int x,int last,int minn,int maxn){
if(minn>maxn) return; cnt++;
add(m-minn+1,1,0),add(maxn,1,1);
for(int i=fs[x];i!=-1;i=nt[i]){
if(vis[to[i]]||to[i]==last) continue;
ins(to[i],x,max(minn,L[i]),min(maxn,R[i]));
}
}
void del(int x,int last,int minn,int maxn){
if(minn>maxn) return;
add(m-minn+1,-1,0),add(maxn,-1,1);
for(int i=fs[x];i!=-1;i=nt[i]){
if(vis[to[i]]||to[i]==last) continue;
del(to[i],x,max(minn,L[i]),min(maxn,R[i]));
}
}
void solve(int x){
vis[x]=true,cnt=1;
for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) dfs(to[i],x,L[i],R[i]),ins(to[i],x,L[i],R[i]);
for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) del(to[i],x,L[i],R[i]);
for(int i=fs[x];i!=-1;i=nt[i]) if(!vis[to[i]]) sum=sz[to[i]],rt=0,fdrt(to[i],x),solve(rt);
}
int main(){
n=read(),sum=n,m=(n<<1)-2,memset(fs,-1,sizeof(fs)),mxs[0]=n+1;
for(int i=1;i<n;i++){
u[i]=read(),v[i]=read(),D[i]=read(),U[i]=read();
id[(i<<1)-1]=(i<<1)-1,id[i<<1]=(i<<1);
od[(i<<1)-1]=-D[i],od[i<<1]=U[i];
}
sort(id+1,id+m+1,cmp);
for(int i=1;i<=(n<<1);i++){
if(id[i]&1) D[(id[i]+1)>>1]=i;
else U[id[i]>>1]=i;
}
for(int i=1;i<n;++i){
nt[tmp]=fs[u[i]],fs[u[i]]=tmp,to[tmp]=v[i],L[tmp]=D[i],R[tmp++]=U[i];
nt[tmp]=fs[v[i]],fs[v[i]]=tmp,to[tmp]=u[i],L[tmp]=D[i],R[tmp++]=U[i];
}
fdrt(1,0),solve(rt),printf("%lld\n",ans); return 0;
}

解法$2-$动态树

动态树的解法非常巧妙,把每一条$(u,v,L,R)$的边看做一次$(u,v,L)$的$Link$和$(u,v,R)$的$Cut$,这样,我们先把每一条边拆成两条,按照权值对操作排序,贡献值和恰好就是每次$Link(u,v)$时$u,v$所在的两个连通块点数的乘积,每条合法的路径一定会被路径上的$\max(L)$的$Link$操作所统计。

复杂度为常规$LCT$的$O(N\log N)$

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define M 200020
#define ls c[x][0]
#define rs c[x][1]
using namespace std;
int read(){
int nm=0,fh=1; char cw=getchar();
for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
return nm*fh;
}
LL ans;
int n,m,fa[M],c[M][2],sz[M],tk[M],rev[M],vm[M],S[M],top;
void pushdown(int x){if(rev[x]) swap(ls,rs),rev[ls]^=1,rev[rs]^=1,rev[x]=0;}
void pushup(int x){sz[x]=sz[ls]+sz[rs]+vm[x]+1;}
bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
void rotate(int x){
int tp=fa[x],dtp=fa[fa[x]],ms,ds;
if(!isroot(tp)){if(c[dtp][0]==tp) c[dtp][0]=x;else c[dtp][1]=x;}
if(c[tp][0]==x) ds=1,ms=0; else ds=0,ms=1;
fa[x]=dtp,fa[tp]=x,fa[c[x][ds]]=tp;
c[tp][ms]=c[x][ds],c[x][ds]=tp,pushup(tp),pushup(x);
}
void splay(int x){
S[top=1]=x;
for(int y=x;!isroot(y);y=fa[y]) S[++top]=fa[y];
while(top) pushdown(S[top]),top--;
while(!isroot(x)){
int tp=fa[x];
if(isroot(tp)) return rotate(x);
else if(c[c[fa[tp]][0]][0]==x) rotate(tp);
else if(c[c[fa[tp]][1]][1]==x) rotate(tp);
else rotate(x);
}
}
void access(int x){for(int y=0;x;y=x,x=fa[x]) splay(x),vm[x]+=sz[rs]-sz[y],rs=y,pushup(x);}
void chroot(int x){access(x),splay(x),rev[x]^=1;}
void cut(int x,int y){chroot(y),access(x),splay(x),ls=fa[y]=0,pushup(x);}
void link(int x,int y){chroot(x),chroot(y),ans+=(LL)sz[x]*(LL)sz[y],fa[x]=y,vm[y]+=sz[x],pushup(y);}
struct opt{
int Typ,X,Y,G; opt(){}
opt(int _Typ,int _X,int _Y,int _G){Typ=_Typ,X=_X,Y=_Y,G=_G;}
void ect(){if(Typ==0) link(X,Y); else cut(X,Y);}
}q[M<<1];
bool cmp(opt i,opt j){return i.G==j.G?i.Typ<j.Typ:i.G<j.G;}
int main(){
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),D=read(),U=read();
m++,q[m]=opt(0,u,v,D),m++,q[m]=opt(1,u,v,U);
} sort(q+1,q+m+1,cmp);
for(int i=1;i<=n;i++) sz[i]=1;
for(int i=1;i<=m;i++) q[i].ect();
printf("%lld\n",ans);return 0;
}

股神小D的更多相关文章

  1. 股神小L 2016Vijos省选集训 day1

    股神小L (stock.c/pas/cpp)============================ 小L厌倦了算法竞赛,希望到股市里一展身手.他凭借自己还行的计算机功底和可以的智商,成功建立一个模型 ...

  2. 2016vijos 1-2 股神小L(堆)

    维护前i天的最优解,那么在后面可能会对前面几天的买卖情况进行调整 如果前面买入,买入的这个在后面一定不会卖出 如果前面卖出,卖出的这个可能会在后面变成买入,因为买这个,卖后面的会获得更多的收益 用一个 ...

  3. 股神小L

    题解 贪心 若当前手中还持有股,则一定会卖出去. 否则,考虑之前卖出的最便宜的股,若售价比当前的股高,就买下这个股,否则我们就把之前卖出的最便宜的股改为买入,这样一定会有股,然后再把这个股卖出即可. ...

  4. [2016北京集训试题14]股神小D-[LCT]

    Description Solution 将(u,v,l,r)换为(1,u,v,l)和(2,u,v,r).进行排序(第4个数为第一关键字,第1个数为第二关键字).用LCT维护联通块的合并和断开.(维护 ...

  5. 股神小D [点分治 or LCT]

    题面 思路 点分治非常$naive$,不讲了,基本思路就是记录路径最小最大值.....然后没了 重点讲一下LCT的做法(好写不卡常)(点分一堆人被卡到飞起hhhh) 首先,这个路径限制由边限制决定,而 ...

  6. 股神小L [贪心]

    题面 思路 股票题肯定是贪心或者$dp$啊 这个题比较$naive$,可以看出来你这里买股票的过程一定是能不买就不买,能卖就拣最贵的日子卖,而且时间不能倒流(废话= =||) 所以我们按照时间从前往后 ...

  7. 2016北京集训测试赛(十四)Problem B: 股神小D

    Solution 正解是一个\(\log\)的link-cut tree. 将一条边拆成两个事件, 按照事件排序, link-cut tree维护联通块大小即可. link-cut tree维护子树大 ...

  8. 2016北京集训测试赛(十四)Problem A: 股神小L

    Solution 考虑怎么卖最赚钱: 肯定是只卖不买啊(笑) 虽然说上面的想法很扯淡, 但它确实能给我们提供一种思路, 我们能不买就不买; 要买的时候就买最便宜的. 我们用一个优先队列来维护股票的价格 ...

  9. (2016北京集训十四)【xsy1556】股神小D - LCT

    题解: 题解居然是LCT……受教了 把所有区间按照端点排序,动态维护目前有重叠的区间,用LCT维护即可. 代码: #include<algorithm> #include<iostr ...

随机推荐

  1. 关于angularjs的model的一些问题

    有的时候 在一些页面中 我们会需要用到弹出的模态框,这里主要是使用angularjs的uimodel. 页面效果如下: 首先我们需要在JS的controller中导入$uibModal模块. HTML ...

  2. 【BZOJ2401】陶陶的难题I 欧拉函数+线性筛

    [BZOJ2401]陶陶的难题I 题意:求,n<=1000000,T<=100000 题解:直接做是n*sqrt(n)的,显然会TLE,不过这题a和b都是循环到n,那么就可以进行如下的神奇 ...

  3. Pipeline模式(netty源码死磕6)

    精进篇:netty源码死磕6  巧夺天工--Pipeline模式揭秘 1. 巧夺天工--Pipeline模式揭秘 1.1. Pipeline模式简介 管道的发名者叫,Malcolm Douglas M ...

  4. web前端开发-Ajax(2)

    前面的一篇博文简单的简绍了Ajax基于jQuery的用法,接下来要对Ajax做进一步的介绍,Ajax请求大致可以通过三种方式发送:原生Ajax,jQuery,伪Ajax.1.原生Ajax: 由于Aja ...

  5. JAXB解析XML为对象

    JAXB支持注解将XML转化为对象,具体看一个简单的例子: <?xml version="1.0" encoding="utf-8"?> <A ...

  6. ssm框架与shiro的整合小demo,用idea开发+maven管理

    shiro安全框架是目前为止作为登录注册最常用的框架,因为它十分的强大简单,提供了认证.授权.加密和会话管理等功能 . shiro能做什么? 认证:验证用户的身份 授权:对用户执行访问控制:判断用户是 ...

  7. matlab 调用 python

    众所周知,Python凭借其众多的第三方模块,近年来被数据分析.机器学习.深度学习等爱好者所喜爱,最主要的是Python还是开源的.另一方面,MATLAB因其在仿真方面的独特优势也被众多人追捧.而在国 ...

  8. Python日期和时间

    日期和时间主要有两个库,datetime和time. datetime: 日期:datetime.date.today() 日期和时间:datetime.datetime.now() 1000天之后: ...

  9. PullToRefresh下拉刷新

    https://github.com/chrisbanes/Android-PullToRefresh

  10. persisted? vs new_record?

    https://joe11051105.gitbooks.io/you-need-to-know-about-ruby-on-rails/content/activerecord/persisted_ ...