您的位置:首页 > 其它

2017.1.21测试题 攻略城池 (树型dp+二分+平衡树+启发式合并)

2017-01-21 22:35 288 查看
题目大意:

有n个点(城市),n-1条边,每条边有一个长度li,代表距离。

每个点有一个di(驻守军队),保证叶子节点的di为0。

初始时间为0,当di为0时,在该时刻的下一秒会每秒产生一个士兵,并且这个士兵会向1号节点(首都)进发,走一条边需要花费li时间,每到一个节点,这个节点的di–,直到为0(为0时视为该城市被占领,为0后下一秒将会开始产生士兵),士兵走到1号节点结束。

问最短需要多长时间所有城市都被占领。

对于100% 的数据,n <=100000,1<=li <=103,di <=108。

(不知道出题人脑洞多大……这道题整整写了1000字,而且神设定……我转化成这样的题目大意不知有没有人能看懂……)

题目分析:(树型dp+二分+平衡树+启发式合并)

我们要求出所有节点被攻占的时间,然后取最大值。

显然我们要求一个节点被攻占的时间就是求最短有多长时间有di个士兵到达这个节点。

因为所有的儿子节点都能够给该节点传送士兵,所以要处理完儿子节点才能处理父亲节点。

我们可以维护一个序列,这个序列中的每个数代表每个从第几秒开始会有一个士兵到达这个节点。

这样一个节点的序列就等于所有儿子节点的序列中的每个数加上路径的长度,自己被攻占之后,还要加上自己被攻占的时间。

因为要查询答案,所以我们要使这个序列有序,选择用平衡树来维护这个序列,treap和Splay都可以,我选择用treap。

这样每次二分答案,然后在这个在平衡树上找到所有的比这个时间小的数,在这个时间之前到达士兵的总个数就为(时间*这些数的个数-这些数的和)。

这样每次把儿子节点的平衡树并到自己身上,要用启发式合并,否则会比较慢。

时间复杂度O(nlog^2n)

注意事项:

int肯定是不够啦,要开long long;

因为只有8M栈空间,所以dfs会爆栈的,用bfs比较靠谱;

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#define N 120000
using namespace std;
const long long INF=100000000000000ll;
inline long long Max(long long x,long long y) { return x>y?x:y; }
struct Treap{
Treap *ch[2];
long long sum,sz,cnt,val;
int key;
long long mark;
Treap(int);
int cmp(long long x)
{
if(x<val) return 0;
if(x>val) return 1;
return -1;
}
void add_mark(long long);
void push_down();
void maintain();
}*null=new Treap(0),*root
;
Treap ::  Treap(int x)
{
sz=cnt=null?1:0;
val=x;sum=val;
ch[0]=ch[1]=null;
mark=0;
key=rand();
}
void Treap :: add_mark(long long x)
{
val+=x; sum+=x*sz;
mark+=x;
return;
}
void Treap :: push_down()
{
if(!mark) return;
if(ch[0]!=null) ch[0]->add_mark(mark);
if(ch[1]!=null) ch[1]->add_mark(mark);
mark=0;
return;
}
void Treap :: maintain()
{
push_down();
sum=ch[0]->sum+ch[1]->sum+cnt*val;
sz=ch[0]->sz+ch[1]->sz+cnt;
}
void turn(Treap *&c,int d)
{
Treap *y=c->ch[d^1];
c->ch[d^1]=y->ch[d];
y->ch[d]=c;
c->maintain();
y->maintain();
c=y;
return;
}
void Insert(Treap *&c,long long x)
{
if(c==null) {c=new Treap(x);return;}
c->push_down();
int d=c->cmp(x);
if(d==-1)
{
c->cnt++;c->sz++;
c->sum+=c->val;
}
else
{
Insert(c->ch[d],x);
c->maintain();
if(c->ch[d]->key<c->key) turn(c,d^1);
}
return;
}
long long query(Treap *c,long long x)
{
if(c==null) return 0;
c->push_down();
int d=c->cmp(x);
if(d!=1) return query(c->ch[0],x);
else     return (c->sz-c->ch[1]->sz)*x-c->sum+c->ch[1]->sum+query(c->ch[1],x);
}
void Merge(Treap *&V,Treap *&c)
{
if(c==null) return;
c->push_down();
Merge(V,c->ch[0]);
Merge(V,c->ch[1]);
for(int i=1;i<=c->cnt;i++) Insert(V,c->val);
delete c;
return;
}
int n,x,y,z;
long long ans=0;
long long w
;
int fir
,nes[N<<1],v[N<<1],q[N<<1],in_d
,edge_top=1;
bool pc[N<<1],b
;
long long ove_tim
;
void edge(int x,int y,int z)
{
edge_top++;
int t=edge_top;
v[t]=y; q[t]=z;
nes[t]=fir[x]; fir[x]=t;
in_d[y]++;
return;
}
#define edge(x,y,z) edge(x,y,z),edge(y,x,z);
void bfs()
{
in_d[1]++;
static int dl
;
int l=1,r=0;
for(int i=2;i<=n;i++)
if(in_d[i]==1) dl[++r]=i;
while(l<=r)
{
int c=dl[l++];
long long ll=0,rr=INF;
while(ll<=rr)
{
long long mid=(ll+rr)>>1;
if(query(root[c],mid)>=w[c]) ove_tim[c]=mid,rr=mid-1;
else ll=mid+1;
}
Insert(root[c],ove_tim[c]);
for(int t=fir[c];t;t=nes[t])
{
if(pc[t]) continue;
pc[t^1]=true;
root[c]->add_mark(q[t]);
if(root[v[t]]->sz<root[c]->sz) swap(root[c],root[v[t]]);
Merge(root[v[t]],root[c]);
in_d[v[t]]--;
if(in_d[v[t]]==1) dl[++r]=v[t];
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
root[i]=null;
scanf("%d",&w[i]);
}
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
edge(x,y,z);

}
bfs();
for(int i=1;i<=n;i++) ans=Max(ans,ove_tim[i]);
cout<<ans;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  测试