1146: [CTSC2008]网络管理Network
Time Limit: 50 Sec Memory Limit: 162 MBSubmit: 3495 Solved: 1032
[Submit][Status][Discuss]
Description
M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门。为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络。该网络的结构由N个路由器和N-1条高速光缆组成。每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络。该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信。 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略。但是由于路由器老化,在这些路由器上进行数据交换会带来很大的延迟。而两个路由器之间的通信延迟时间则与这两个路由器通信路径上所有路由器中最大的交换延迟时间有关。作为M公司网络部门的一名实习员工,现在要求你编写一个简单的程序来监视公司的网络状况。该程序能够随时更新网络状况的变化信息(路由器数据交换延迟时间的变化),并且根据询问给出两个路由器通信路径上延迟第k大的路由器的延迟时间。【任务】 你的程序从输入文件中读入N个路由器和N-1条光缆的连接信息,每个路由器初始的数据交换延迟时间Ti,以及Q条询问(或状态改变)的信息。并依次处理这Q条询问信息,它们可能是: 1. 由于更新了设备,或者设备出现新的故障,使得某个路由器的数据交换延迟时间发生了变化。 2. 查询某两个路由器a和b之间的路径上延迟第k大的路由器的延迟时间。
Input
第一行为两个整数N和Q,分别表示路由器总数和询问的总数。第二行有N个整数,第i个数表示编号为i的路由器初始的数据延迟时间Ti。紧接着N-1行,每行包含两个整数x和y。表示有一条光缆连接路由器x和路由器y。紧接着是Q行,每行三个整数k、a、b。如果k=0,则表示路由器a的状态发生了变化,它的数据交换延迟时间由Ta变为b。如果k>0,则表示询问a到b的路径上所经过的所有路由器(包括a和b)中延迟第k大的路由器的延迟时间。注意N,Q<=80000,任意一个路由器在任何时刻都满足延迟时间小于10^8。对于所有询问满足0<=K<=N
Output
对于每一个第二种询问(k>0),输出一行。包含一个整数为相应的延迟时间。如果路径上的路由器不足k个,则输出信息“invalid request!”(全部小写不包含引号,两个单词之间有一个空格)。
Sample Input
5 5
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5
5 1 2 3 4
3 1
2 1
4 3
5 3
2 4 5
0 1 2
2 2 3
2 1 4
3 3 5
Sample Output
3
2
2
invalid request!
2
2
invalid request!
HINT
Source
Solution
这道题方法很好想把,树上带修第k大问题,最直观的就是 树链剖分+树套树 复杂度$O(Nlog^{4}N)$,我是直接 dfs序+带修主席树 硬上,复杂度是$O(Nlog^{2}N)$
问题在于 内存 ,所以可以考虑先建一棵完整的树,然后 树状数组修改时只搞一条链 这样能把内存大大降低。
并且 这里的dfs序直接用就好了,并不需要搞成入栈出栈序,那样内存消耗依旧很大,自己第一次大概是180M--MLE。
Code
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<vector> using namespace std; inline int read() { int x=0,f=1; char ch=getchar(); while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f; } #define MAXN 100010 #define vec vector<int> int N,Q,ls[MAXN<<1],tp,val[MAXN]; struct QNode{int x,y,z,opt;}q[MAXN]; struct EdgeNode{int next,to;}edge[MAXN<<1]; int head[MAXN],cnt=1; inline void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;} inline void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);} int pl[MAXN],dfn,pr[MAXN],deep[MAXN],father[17][MAXN]; inline void DFS(int now,int last) { pl[now]=++dfn; for (int i=1; i<=16; i++) if (deep[now]>=(1<<i)) father[i][now]=father[i-1][father[i-1][now]]; else break; for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last) father[0][edge[i].to]=now,deep[edge[i].to]=deep[now]+1, DFS(edge[i].to,now); pr[now]=dfn; } inline int LCA(int x,int y) { if (deep[x]<deep[y]) swap(x,y); int dd=deep[x]-deep[y]; for (int i=0; i<=16; i++) if (dd&(1<<i)) x=father[i][x]; for (int i=16; i>=0; i--) if (father[i][x]!=father[i][y]) x=father[i][x],y=father[i][y]; return x==y? x:father[0][x]; } namespace PrTree { int lson[MAXN*80],rson[MAXN*80],sum[MAXN*80],sz,root[MAXN]; vec add,sub; inline void Insert(int l,int r,int &now,int fa,int pos,int val) { now=++sz; sum[now]=sum[fa]+val; if (l==r) {return;} lson[now]=lson[fa],rson[now]=rson[fa]; int mid=(l+r)>>1; if (pos<=mid) Insert(l,mid,lson[now],lson[fa],pos,val); else Insert(mid+1,r,rson[now],rson[fa],pos,val); } inline int Query(int l,int r,int kth) { if (l==r) return l; int mid=(l+r)>>1,sn=0,sr=0; for (int i=0; i<add.size(); i++) sr+=sum[rson[add[i]]],sn+=sum[add[i]]; for (int i=0; i<sub.size(); i++) sr-=sum[rson[sub[i]]],sn-=sum[sub[i]]; if (sn<kth) return -1; if (kth>sr) { for (int i=0; i<add.size(); i++) add[i]=lson[add[i]]; for (int i=0; i<sub.size(); i++) sub[i]=lson[sub[i]]; Query(l,mid,kth-sr); } else { for (int i=0; i<add.size(); i++) add[i]=rson[add[i]]; for (int i=0; i<sub.size(); i++) sub[i]=rson[sub[i]]; Query(mid+1,r,kth); } } inline void BuildTree(int now,int last) { Insert(1,tp,root[now],root[last],val[now],1); for (int i=head[now]; i; i=edge[i].next) if (edge[i].to!=last) BuildTree(edge[i].to,now); } }using namespace PrTree; namespace BIT { int tree[MAXN]; inline int lowbit(int x) {return x&-x;} inline void Modify(int x,int pos,int D) { for (int i=x; i<=dfn; i+=lowbit(i)) Insert(1,tp,tree[i],tree[i],pos,D); } inline int Add(int x) { add.push_back(root[x]); for (int i=pl[x]; i; i-=lowbit(i)) add.push_back(tree[i]); } inline int Sub(int x) { sub.push_back(root[x]); for (int i=pl[x]; i; i-=lowbit(i)) sub.push_back(tree[i]); } inline void Clear() {add.clear(); sub.clear();} }using namespace BIT; int main() { N=read(); Q=read(); for (int i=1; i<=N; i++) val[i]=read(),ls[++tp]=val[i]; for (int i=1,x,y; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y); DFS(1,0); for (int i=1; i<=Q; i++) { q[i].opt=read(),q[i].x=read(),q[i].y=read(); if (q[i].opt) q[i].z=LCA(q[i].x,q[i].y); else ls[++tp]=q[i].y; } sort(ls+1,ls+tp+1); tp=unique(ls+1,ls+tp+1)-ls-1; for (int i=1; i<=N; i++) val[i]=lower_bound(ls+1,ls+tp+1,val[i])-ls; for (int i=1; i<=Q; i++) if (!q[i].opt) q[i].y=lower_bound(ls+1,ls+tp+1,q[i].y)-ls; BuildTree(1,0); for (int i=1; i<=Q; i++) { int opt=q[i].opt,x=q[i].x,y=q[i].y,z=q[i].z; if (!opt) Modify(pl[x],val[x],-1),Modify(pr[x]+1,val[x],1),Modify(pl[x],y,1),Modify(pr[x]+1,y,-1),val[x]=y; else { Clear(); Add(x); Add(y); Sub(z); Sub(father[0][z]); int ans=Query(1,tp,opt); if (ans==-1) puts("invalid request!"); else printf("%d ",ls[ans]); } } return 0; }
大概Code了1h+,然后CodeVS上测了一下1,2点WA,其余AC,不解,一顿输入输出调试了快2h,然后莫名其妙就A了,然后撤回了一下最早的版本,竟然也A了,并不知道这两个小时到底做了些什么= =
UPD:
终于想到自己做了些什么了,自己搞了搞事,没搞出来,又找标称对拍没搞出来,然后把标程输入输出中间值对拍无果,然后弃疗看题解,题解说dfs从 $frac {n+1}{2}$开始,然后自己试了试然后AC了,然后自己在想为什么会这样??然后又手动撤销回最初的版本,然后又交了一遍,然后也A了..黑人问号.jpg
决定贴一下垃圾的数据生成器:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<ctime> using namespace std; int main() { srand(time(0)); int N=rand()%10000+1,Q=rand()%10000+1; printf("%d %d ",N,Q); for (int i=1; i<=N; i++) printf("%d ",rand()); puts(""); for (int i=2,x; i<=N; i++) x=rand()%(i-1)+1,printf("%d %d ",i,x); for (int i=1; i<=Q; i++) { int x=rand()%2; if (x) printf("%d %d %d ",0,rand()%N+1,rand()); else printf("%d %d %d ",rand()%4+1,rand()%N+1,rand()%N+1); } return 0; }