BZOJ 3653 谈笑风生
2018-03-19 20:40
357 查看
Problem
BZOJ然而是权限题,如果你像蒟蒻我一样没有权限号请上洛谷~
Solution
据说正解是主席树???不管了,反正主席树常数巨大,线段树虽然暴力好歹也能卡过去,而且这题数据还比较水。
其实是我不会打主席树
法一
显然,分两种情况,若b是a的祖先,那么贡献就是sz[a]-1,而若b在a的子树中,则贡献为sz-1。
第一种情况很好解决,考虑第二种情况。
我们将树按照dfs序拍扁成数组,然后在数组上根据dfs序中答案的最小最大深度,进行dfs搜索。注意要满足depth<=mx[rt]的限制条件才能返回区间的值,否则可能会多计算了某些深度之差大于k的值。另外要用mn[rt]来剪枝,也就是说如果区间中最小的深度都已经大于了depth,显然就不需要继续向下搜了。
然而这可以被数据卡掉,所以就介绍不会被卡的法二……
[b]法二
Orz了一发boshi的题解
同样也是利用了dfs序,大致思想就是将询问离线,然后树形dp:维护一个序列,a[i]表示深度为i的节点的sz之和,那么答案显然就是sum[a[i]+k]-sum[a[i]],考虑用树状数组维护,退出该节点时插入此节点的贡献即可。答案不能受非子树,深度符合要求的贡献影响,在进入节点时减去其贡献即可。
时间复杂度O(nlogn)O(nlogn)
Code
#include d8cd <cstdio> using namespace std; typedef long long ll; template <typename Tp> inline void read(Tp &x) { x=0;char ch=getchar(); while(ch<'0'||ch>'9') ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); } const int maxn=300010; struct data{int v,nxt;}edge[maxn<<1]; int n,m,p,dfc,sz[maxn],deep[maxn],dfn[maxn][2],head[maxn]; int xu[maxn<<1],a[maxn<<1],mx[maxn<<3],mn[maxn<<3]; ll ans,sum[maxn<<3]; inline int min(int x,int y){return x<y?x:y;} inline int max(int x,int y){return x>y?x:y;} inline void insert(int u,int v) { edge[++p]=(data){v,head[u]};head[u]=p; edge[++p]=(data){u,head[v]};head[v]=p; } void dfs(int x,int pre) { dfn[x][0]=++dfc;sz[x]=1; for(int i=head[x];i;i=edge[i].nxt) if(edge[i].v!=pre) { deep[edge[i].v]=deep[x]+1; dfs(edge[i].v,x); sz[x]+=sz[edge[i].v]; } dfn[x][1]=++dfc; xu[dfn[x][0]]=xu[dfn[x][1]]=deep[x]; a[dfn[x][0]]=sz[x]-1;a[dfn[x][1]]=0; } void build(int l,int r,int rt) { if(l==r){mx[rt]=mn[rt]=xu[l];sum[rt]=a[l];return ;} int m=(l+r)>>1; build(l,m,rt<<1); build(m+1,r,rt<<1|1); sum[rt]=sum[rt<<1]+sum[rt<<1|1]; mx[rt]=max(mx[rt<<1],mx[rt<<1|1]); mn[rt]=min(mn[rt<<1],mn[rt<<1|1]); } ll query(int l,int r,int L,int R,int now,int depth) { if(mn[now]>depth) return 0; if(L<=l&&r<=R&&mx[now]<=depth) return sum[now]; int m=(l+r)>>1;ll res=0; if(L<=m) res+=query(l,m,L,R,now<<1,depth); if(m<R) res+=query(m+1,r,L,R,now<<1|1,depth); return res; } int main() { read(n);read(m); for(int i=1,x,y;i<n;i++) {read(x);read(y);insert(x,y);} deep[1]=1;dfs(1,0); build(1,dfc,1); for(int i=1,x,y;i<=m;i++) { read(x),read(y); ans=(ll)min(deep[x]-1,y)*(sz[x]-1); ans+=query(1,dfc,dfn[x][0]+1,dfn[x][1],1,deep[x]+y); printf("%lld\n",ans); } return 0; }
boshi’s code
#include <iostream> #include <cstring> #include <cstdio> #include <vector> #define MX 300003 using namespace std; typedef long long ll; int fst[MX],nxt[MX*2],v[MX*2],siz[MX],dep[MX],lnum; ll sum[MX]; vector<int>qk[MX],qid[MX]; ll ans[MX]; int n,m; void add(int p,ll x){for(;p<MX;p+=(p&(-p)))sum[p]+=x;} ll qsm(int p){ll x=0;for(;p;p-=(p&(-p)))x+=sum[p];return x;} void addeg(int nu,int nv) { nxt[++lnum]=fst[nu]; fst[nu]=lnum; v[lnum]=nv; } void dfs(int x,int fa,int d) { siz[x]=1,dep[x]=d; for(int i=fst[x];i!=-1;i=nxt[i]) if(v[i]!=fa) dfs(v[i],x,d+1), siz[x]+=siz[v[i]]; } void calc(int x,int fa) { for(int i=0;i<qk[x].size();i++)ans[qid[x][i]]-=(qsm(min(dep[x]+qk[x][i],MX-1))-qsm(dep[x])); for(int i=fst[x];i!=-1;i=nxt[i]) if(v[i]!=fa) calc(v[i],x); for(int i=0;i<qk[x].size();i++)ans[qid[x][i]]+=(qsm(min(dep[x]+qk[x][i],MX-1))-qsm(dep[x])+(ll)min(dep[x]-1,qk[x][i])*(ll)(siz[x]-1)); add(dep[x],siz[x]-1); } void input() { int a,b; memset(fst,0xff,sizeof(fst)),lnum=-1; scanf("%d%d",&n,&m); for(int i=1;i<n;i++) { scanf("%d%d",&a,&b); addeg(a,b); addeg(b,a); } for(int i=1;i<=m;i++) { scanf("%d%d",&a,&b); qk[a].push_back(b); qid[a].push_back(i); } } int main() { input(); dfs(1,0,1); calc(1,0); for(int i=1;i<=m;i++)printf("%lld\n",ans[i]); return 0; }
相关文章推荐
- bzoj 3653: 谈笑风生 可持久化线段树
- BZOJ3653: 谈笑风生
- bzoj 3653 [湖南集训]谈笑风生
- 【BZOJ】【3653】谈笑风生
- BZOJ3653 : 谈笑风生
- 【bzoj3653】谈笑风生
- 【bzoj3653】谈笑风生
- [BZOJ3653]谈笑风生(dfs序+主席树)
- 【BZOJ3653】谈笑风生【主席树】【DFS序】
- 【BZOJ3653】谈笑风生 离线+树状数组+DFS序
- Bzoj3653 谈笑风生
- BZOJ_3653_谈笑风生_树状数组
- BZOJ 3653: 谈笑风生(DFS序+可持久化线段树)
- 【主席树】bzoj3653 谈笑风生
- BZOJ3653 & 洛谷3899:谈笑风生——题解
- 【BZOJ 3653】 谈笑风生|主席树
- bzoj 3653: 谈笑风生
- BZOJ3653: 谈笑风生
- BZOJ3653 谈笑风生
- [bzoj3653]谈笑风生 主席树