股神小D

题目大意:
给定一棵树,每一条边有$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的更多相关文章
- 股神小L 2016Vijos省选集训 day1
股神小L (stock.c/pas/cpp)============================ 小L厌倦了算法竞赛,希望到股市里一展身手.他凭借自己还行的计算机功底和可以的智商,成功建立一个模型 ...
- 2016vijos 1-2 股神小L(堆)
维护前i天的最优解,那么在后面可能会对前面几天的买卖情况进行调整 如果前面买入,买入的这个在后面一定不会卖出 如果前面卖出,卖出的这个可能会在后面变成买入,因为买这个,卖后面的会获得更多的收益 用一个 ...
- 股神小L
题解 贪心 若当前手中还持有股,则一定会卖出去. 否则,考虑之前卖出的最便宜的股,若售价比当前的股高,就买下这个股,否则我们就把之前卖出的最便宜的股改为买入,这样一定会有股,然后再把这个股卖出即可. ...
- [2016北京集训试题14]股神小D-[LCT]
Description Solution 将(u,v,l,r)换为(1,u,v,l)和(2,u,v,r).进行排序(第4个数为第一关键字,第1个数为第二关键字).用LCT维护联通块的合并和断开.(维护 ...
- 股神小D [点分治 or LCT]
题面 思路 点分治非常$naive$,不讲了,基本思路就是记录路径最小最大值.....然后没了 重点讲一下LCT的做法(好写不卡常)(点分一堆人被卡到飞起hhhh) 首先,这个路径限制由边限制决定,而 ...
- 股神小L [贪心]
题面 思路 股票题肯定是贪心或者$dp$啊 这个题比较$naive$,可以看出来你这里买股票的过程一定是能不买就不买,能卖就拣最贵的日子卖,而且时间不能倒流(废话= =||) 所以我们按照时间从前往后 ...
- 2016北京集训测试赛(十四)Problem B: 股神小D
Solution 正解是一个\(\log\)的link-cut tree. 将一条边拆成两个事件, 按照事件排序, link-cut tree维护联通块大小即可. link-cut tree维护子树大 ...
- 2016北京集训测试赛(十四)Problem A: 股神小L
Solution 考虑怎么卖最赚钱: 肯定是只卖不买啊(笑) 虽然说上面的想法很扯淡, 但它确实能给我们提供一种思路, 我们能不买就不买; 要买的时候就买最便宜的. 我们用一个优先队列来维护股票的价格 ...
- (2016北京集训十四)【xsy1556】股神小D - LCT
题解: 题解居然是LCT……受教了 把所有区间按照端点排序,动态维护目前有重叠的区间,用LCT维护即可. 代码: #include<algorithm> #include<iostr ...
随机推荐
- ios cocoapods获取不到最新库的原因主要有两个:
获取不到最新库的原因主要有两个: 1.cocoapods的版本过低 2.还没有更新本地仓库 解决方法: 1.更新pods 在终端执行命令行 sudo gem install cocoapods 执行完 ...
- Github上好的Android开源框架
1.volley 项目地址 https://github.com/smanikandan14/Volley-demo (1) JSON,图像等的异步下载: (2) 网络请求的排序(scheduli ...
- [精]poj2724
Purifying Machine Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 5027 Accepted: 1455 ...
- python的进程和线程
关于进程: An executing instance of a program is called a process.程序的执行实例称为进程. Each process provides the ...
- 九度OJ 1208:10进制 VS 2进制 (进制转换)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:2040 解决:612 题目描述: 对于一个十进制数A,将A转换为二进制数,然后按位逆序排列,再转换为十进制数B,我们乘B为A的二进制逆序数. ...
- 九度OJ 1193:矩阵转置 (矩阵计算)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1673 解决:1132 题目描述: 输入一个N*N的矩阵,将其转置后输出.要求:不得使用任何数组(就地逆置). 输入: 输入的第一行包括一个 ...
- mybatis相关
1 namespace dao中使用namespace+id一起来完成对mapper中sql statement的调用. 2 关于resultMap和parameterType parameterTy ...
- 浅谈公平组合游戏IGC
浅谈公平组合游戏IGC IGC简介 一个游戏满足以下条件时被叫做IGC游戏 (前面三个字是自己YY的,不必在意) 竞争性:两名玩家交替行动. 公平性:游戏进程的任意时刻,可以执行的操作和操作者本人无关 ...
- Django之restframework2视图三部曲
视图三部曲 下面我来来看restframework是如何将冗余的代码一步步的进行封装. 这里主要用到的是多继承 第一步mixin类编写视图 AuthorModelSerializer: class A ...
- Js 抱错:::SyntaxError: identifier starts immediately after numeric literal
SyntaxError: identifier starts immediately after numeric literal 今天写了个onclick()方法,有这样的一个变量4028b88161 ...