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比较靠谱;
代码如下:
有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; }
相关文章推荐
- Android之使用Http协议实现文件上传功能
- mysql集群之MMM简单搭建
- 通晓网络测试常用命令
- Node.js测试中的Mock文件系统详解
- Nodejs学习笔记之测试驱动
- 自动化测试读写64位操作系统的注册表
- 可以测试javascript运行效果的代码
- 使用php测试硬盘写入速度示例
- python 测试实现方法
- 如何测试端口通不通(四种方法)
- 举例详解PHP脚本的测试方法
- 使用Jasmine和Karma对AngularJS页面程序进行测试
- Php-Redis安装测试笔记
- mysql压力测试脚本实例
- JavaScript 组件之旅(四):测试 JavaScript 组件
- js简单网速测试方法完整实例
- Android 中构建快速可靠的 UI 测试
- Android App开发的自动化测试框架UI Automator使用教程
- Android触屏测试实例代码
- 解析libcurl在android下的移植、编译与测试