[BZOJ 4326]运输计划
很容易想到树剖直接搞一发的玩法。估计复杂度是[tex]O(nlog^2n)[/tex]的,有点吃不消,再加上会被卡常,咔嚓掉。
考虑二分答案。我们可以直接二分最终答案,那么如何验证?
假设验证谓词为[tex]C(t)[/tex],表示通过把一条边清零可以使得用时为[tex]t[/tex]。那么所有用时大于[tex]t[/tex]的计划都要被清掉一条边。可见我们需要清零的边是所有用时大于[tex]t[/tex]的计划的边的交集中的最大边。
那么如何求交集呢?可以考虑树上差分,对于每次计划[tex](u,v)[/tex],我们求出其最近公共祖先[tex]L[/tex],然后对[tex]d[u][/tex]和[tex]d[v][/tex]分别加一,对[tex]d[L][/tex]则减二。随后将d上标记上推,可以求出每个点连向父亲的边被几个计划经过。易求交集。
至此方向已明确:二分答案,树上差分验证,同时我们还要用到LCA。LCA建议使用总计[tex]O(n)[/tex]的TarjanLCA离线算法,倍增也可。
代码:
/**************************************************************
Problem: 4326
User: danihao123
Language: C++
Result: Accepted
Time:4756 ms
Memory:62188 kb
****************************************************************/
#include <cstdio>
#include <cctype>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn=300001,maxm=300001*2;
const int BUFSIZE=10000001;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define GRAPH_REP(i,u) for(i=first[(u)];i;i=next[i])
#define CUS_REP(i,a,b) for(i=(a);i<=(b);i++)
int n;
namespace FastIO{
char buf[BUFSIZE];
int IO_tot=0;
inline void InitFastIO(){
fread(buf,sizeof(char),BUFSIZE-1,stdin);
}
inline char GetC(){
return buf[IO_tot++];
}
inline int readint(){
register int x=0;
register char c=GetC();
while(!isdigit(c))
c=GetC();
while(isdigit(c)){
x=10*x+c-'0';
c=GetC();
}
return x;
}
int bf[10];
inline void putint(int x){
register int siz=0,i;
if(!x){
bf[siz++]=0;
}else{
while(x){
bf[siz++]=x%10;
x/=10;
}
}
for(i=siz-1;i>=0;i--)
putchar(bf[i]+'0');
putchar('\n');
}
};
namespace Tree{
int first[maxn];
int next[maxm],to[maxm],dist[maxm];
int tot=0;
inline void Add_Edge(int u,int v,int d){
tot++;
next[tot]=first[u];
first[u]=tot;
to[tot]=v;
dist[tot]=d;
}
int d[maxn],father[maxn];
vector<int> hx;
void dfs_1(int x,int fa,int dis){
d[x]=dis;
father[x]=fa;
int i;
GRAPH_REP(i,x){
if(to[i]!=fa)
dfs_1(to[i],x,dis+dist[i]);
}
hx.push_back(x);
}
int lazy[maxn];
inline void ClearLazy(){
memset(lazy,0,sizeof(lazy));
}
inline int DealCheck(int x){
register int i,v,ans=0;
REP_B(i,n){
v=hx[i-1];
lazy[father[v]]+=lazy[v];
}
REP_B(i,n){
if(lazy[i]==x){
ans=max(ans,d[i]-d[father[i]]);
}
}
return ans;
}
// Djoin Set
int p[maxn];
int find_set(int x){
if(p[x]==x)
return x;
else
return p[x]=find_set(p[x]);
}
inline void union_set(int x,int y){
p[find_set(y)]=find_set(x);
}
inline void make_set(int x){
p[x]=x;
}
// LCA
struct Query{
int u,v,b;
};
bool vis[maxn];
vector<Query> qs;
vector<int> querys[maxn];
inline void Add_Query(int x,int y,int b){
querys[x].push_back(qs.size());
qs.push_back((Query){x,y,b});
}
int res[maxn][3];
int n;
void LCA(int u){
make_set(u);
vis[u]=true;
int i,temp;
REP(i,querys[u].size()){
Query& tep=qs[querys[u][i]];
if(vis[tep.v])
res[tep.b][2]=find_set(tep.v);
}
for(i=first[u];i;i=next[i]){
temp=to[i];
if(!vis[temp]){
LCA(temp);
union_set(u,temp);
}
}
}
};
using namespace FastIO;
struct Deal{
int u,v,lca,d;
bool operator <(const Deal& x) const{
return d<x.d;
}
};
vector<Deal> V;
int m;
inline bool Check(int x){
register int i,ideal=0,yh=V[m-1].d;
Tree::ClearLazy();
for(i=m-1;i>=0;i--){
int& u=V[i].u,v=V[i].v,lca=V[i].lca,d=V[i].d;
if(d>x){
ideal++;
Tree::lazy[u]++;
Tree::lazy[v]++;
Tree::lazy[lca]-=2;
}else{
break;
}
}
yh-=Tree::DealCheck(ideal);
return yh<=x;
}
int main(){
register int i,u,v,lca,d;
FastIO::InitFastIO();
n=readint();
m=readint();
REP(i,n-1){
u=readint();
v=readint();
d=readint();
Tree::Add_Edge(u,v,d);
Tree::Add_Edge(v,u,d);
}
REP(i,m){
u=readint();
v=readint();
Tree::Add_Query(u,v,i);
Tree::Add_Query(v,u,i);
Tree::res[i][0]=u;
Tree::res[i][1]=v;
}
Tree::dfs_1(1,0,0);
Tree::LCA(1);
REP(i,m){
u=Tree::res[i][0];
v=Tree::res[i][1];
lca=Tree::res[i][2];
d=Tree::d[u]+Tree::d[v]-2*Tree::d[lca];
V.push_back((Deal){u,v,lca,d});
}
sort(V.begin(),V.end());
u=0;
v=V[m-1].d+1;
while(u<v){
d=u+(v-u)/2;
if(Check(d))
v=d;
else
u=d+1;
}
putint(u);
return 0;
}
[BZOJ 3524]Couriers
这道题有了主席树,就是裸题了……
只要用主席树维护出现次数,然后二分求解即可
但是!数组一定要开大,开大,再开大,重要的事情说三遍!
代码: