分块\(yyds\)

————关于线段树合并的题我用分块过掉这件事

题目传送门

先说正解

正解当然是线段树合并等一类做法了

至于解析。。。出门右转题解区第一篇 (就是他让我看不懂,然后用分块打的\(QAQ\))

给出 fengwu 的\(code\)

#include<bits/stdc++.h>
#include<iostream>
using namespace std;
#define re register int
const int N=100005;
int n;
int p[N],d[N];
int to[N],nxt[N],head[N],rp;
int rt[N],ans[N];
bool cmp(int x,int y){return p[x]<p[y];}
void add_edg(int x,int y){
to[++rp]=y;
nxt[rp]=head[x];
head[x]=rp;
}
struct node{
int sum[N*80];
int ls[N*80],rs[N*80];
int cnt;
int insert(int x,int l,int r,int pos){
if(!x)x=++cnt;
sum[x]++;
if(l==r)return x;
int mid=(l+r)>>1;
if(pos<=mid)ls[x]=insert(ls[x],l,mid,pos);
else rs[x]=insert(rs[x],mid+1,r,pos);
sum[x]=sum[ls[x]]+sum[rs[x]];
return x;
}
int query(int x,int l,int r,int pos){
if(!x)return 0;
if(l==r)return sum[x];
int mid=(l+r)>>1;
if(pos<=mid)return query(ls[x],l,mid,pos)+sum[rs[x]];
else return query(rs[x],mid+1,r,pos);
}
int marge(int x,int y,int l,int r){
if(!x)return y+x;
if(!y)return x+y;
if(l==r){
sum[x]+=sum[y];
return x;
}
int mid=(l+r)>>1;
ls[x]=marge(ls[x],ls[y],l,mid);
rs[x]=marge(rs[x],rs[y],mid+1,r);
sum[x]=sum[ls[x]]+sum[rs[x]];
return x;
}
}xds;
void dfs(int x){
for(re i=head[x];i;i=nxt[i])
dfs(to[i]);
rt[x]=xds.insert(rt[x],1,n+1,p[x]);
for(re i=head[x];i;i=nxt[i])
rt[x]=xds.marge(rt[x],rt[to[i]],1,n+1);
ans[x]=xds.query(rt[x],1,n+1,p[x]+1);
}
int main(){
scanf("%d",&n);
for(re i=1;i<=n;i++)
scanf("%d",&p[i]),d[i]=i;
sort(d+1,d+n+1,cmp);
for(re i=1;i<=n;i++)p[d[i]]=i;
for(re i=2;i<=n;i++){
int f;
scanf("%d",&f);
add_edg(f,i);
}
dfs(1);
for(re i=1;i<=n;i++){
printf("%d\n",ans[i]);
}
}

再谈分块

分块么,无非就是优化的暴力,所以调试到崩溃的我斗胆交了一下暴力代码=>\(70pts\)

出乎意料!!!

然后我吸了口毒氧 ,再然后就\(100pts\)了。。。

这个不会的就不用出门了,凑合看看就能懂,下面给出\(code\)以及我的\(link\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int m,n,tot,cnt,tmp,tim,ans,fz[N+5],Ans[N+5],tail[N+5],ys[N+5],num[N+5],nxt[N+5],head[N+5],ver[N+5];
struct jz
{
int id,num,pos;
}s[N+5];
bool comp(jz x,jz y)
{
if(x.id!=y.id)
return x.id<y.id;
}
void lsh()
{
for(int i=1;i<=n;i++)
ys[i]=s[i].num;
sort(ys+1,ys+n+1);
tmp=unique(ys+1,ys+n+1)-ys-1;
for(int i=1;i<=n;i++)
s[i].num=lower_bound(ys+1,ys+tmp+1,s[i].num)-ys;
}
void add_edge(int x,int y)
{
ver[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
int dfs(int x)
{
s[x].id=++tim;
if(head[x]==0)
return tail[x]=x;
for(int i=head[x];i;i=nxt[i])
{
int jud=dfs(ver[i]);
if(s[jud].id>s[tail[x]].id)
tail[x]=jud;
}
return tail[x];
}
int ask(int li,int ri,int k)
{
if(li>ri) return 0;
for(int i=li;i<=ri;i++)
if(s[i].num>=k)
ans++;
return ans;
}
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i].num),s[i].pos=i;
for(int i=2,x;i<=n;i++)
scanf("%d",&x),add_edge(x,i);
lsh();
dfs(1);
sort(s,s+n+1,comp);
for(int i=1;i<=n;i++)
fz[s[i].pos]=s[i].id;
for(int i=1;i<=n;i++)
Ans[s[i].pos]=ask(i+1,fz[tail[s[i].pos]],s[i].num+1);
for(int i=1;i<=n;i++)
printf("%d\n",Ans[i]);
return 0;
}

然后嘞就是详解以及分块做法了(调了好久就因为一个\(j\)打成了\(i\),痛哭\(.jpg\))\(link\)

思路比较简单

先是读入

	scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i].num),s[i].pos=i;
for(int i=2,x;i<=n;i++)
scanf("%d",&x),add_edge(x,i);

然后离散化一下

void lsh()
{
for(int i=1;i<=n;i++)
ys[i]=s[i].num;
sort(ys+1,ys+n+1);
tmp=unique(ys+1,ys+n+1)-ys-1;
for(int i=1;i<=n;i++)
s[i].num=lower_bound(ys+1,ys+tmp+1,s[i].num)-ys;
}

然后就是重点了,先建边,读入的时候一定要从2开始,因为1是没有上司的然后dfs一下求出每个点被便历到的\(s[i].id\)值,同时记录一下\(tail[i]\)(表示在原顺序下的i的最后一个下属)

分为以下两种情况:

  1. 已经是最后一个,没有下属,那么\(head[x]\)一定是0,此时直接\(return\),并且给\(tail[x]\)赋值

  2. 有子节点,此时用一个\(jud\)记录一下子节点返回的\(tail\),然后与现在存的\(tail\)比较\(id\)值,取\(id\)值较大的,即最后扫描到的,最后不要忘了\(return\)一下

int dfs(int x)
{
s[x].id=++tim;
if(head[x]==0)
return tail[x]=x;
for(int i=head[x];i;i=nxt[i])
{
int jud=dfs(ver[i]);
if(s[jud].id>s[tail[x]].id)
tail[x]=jud;
}
return tail[x];
}

然后给\(s\)结构体按照\(id\)顺序排个序,顺便用\(fz\)数组记录一下:\(fz[i]=j\)表示先前为第\(i\)的点现在的下标是\(j\)

接下来就是愉快的分块

	t=sqrt(n);
for(int i=1;i<=t;i++)
{
l[i]=(i-1)*sqrt(n)+1;
r[i]=i*sqrt(n);
}
if(r[t]<n)
{
t++;
l[t]=r[t-1]+1;
r[t]=n;
}

接下来的做法就与教主的魔法相似了,大概意思就是再建一个数组储存每个块排序后的样子,在二分查找一下就好了,不会的嘛,老办法,出门右转题解区\(qwq\)。

最后在拿一个\(Ans\)数组存一下输出就\(OK\)了

\(code\)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5;
int m,t,n,tot,cnt,tmp,tim,ans,fz[N+5],Ans[N+5],tail[N+5],ys[N+5],pos[N+5],l[N+5],r[N+5],num[N+5],nxt[N+5],head[N+5],ver[N+5];
struct jz
{
int id,num,pos;
}s[N+5];
bool comp(jz x,jz y)
{
if(x.id!=y.id)
return x.id<y.id;
return x.num>y.num;
}
void lsh()
{
for(int i=1;i<=n;i++)
ys[i]=s[i].num;
sort(ys+1,ys+n+1);
tmp=unique(ys+1,ys+n+1)-ys-1;
for(int i=1;i<=n;i++)
s[i].num=lower_bound(ys+1,ys+tmp+1,s[i].num)-ys;
}
void add_edge(int x,int y)
{
ver[++cnt]=y;
nxt[cnt]=head[x];
head[x]=cnt;
}
int dfs(int x)
{
s[x].id=++tim;
if(head[x]==0)
return tail[x]=x;
for(int i=head[x];i;i=nxt[i])
{
int jud=dfs(ver[i]);
if(s[jud].id>s[tail[x]].id)
tail[x]=jud;
}
return tail[x];
}
int ask(int li,int ri,int k)
{
if(li>ri) return 0;
int p=pos[li],q=pos[ri],ans=0;
if(p==q)
{
for(int i=li;i<=ri;i++)
if(s[i].num>=k)
ans++;
return ans;
}
for(int i=li;i<=r[p];i++)
if(s[i].num>=k)
ans++;
for(int i=p+1;i<q;i++)
ans+=r[i]+1-(lower_bound(num+l[i],num+r[i]+1,k)-num);
for(int i=l[q];i<=ri;i++)
if(s[i].num>=k)
ans++;
return ans;
}
signed main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&s[i].num),s[i].pos=i;
for(int i=2,x;i<=n;i++)
scanf("%d",&x),add_edge(x,i);
lsh();
dfs(1);
sort(s,s+n+1,comp);
for(int i=1;i<=n;i++)
fz[s[i].pos]=s[i].id;
t=sqrt(n);
for(int i=1;i<=t;i++)
{
l[i]=(i-1)*sqrt(n)+1;
r[i]=i*sqrt(n);
}
if(r[t]<n)
{
t++;
l[t]=r[t-1]+1;
r[t]=n;
}
for(int i=1;i<=t;i++)
{
for(int j=l[i];j<=r[i];j++)
{
pos[j]=i;
num[j]=s[j].num;
}
sort(num+l[i],num+r[i]+1);
}
for(int i=1;i<=n;i++)
Ans[s[i].pos]=ask(i+1,fz[tail[s[i].pos]],s[i].num+1);
for(int i=1;i<=n;i++)
printf("%d\n",Ans[i]);
return 0;
}

不得不说这个题让我的调试能力大增。。。

题解 P3605 [USACO17JAN]Promotion Counting P的更多相关文章

  1. 线段树合并 || 树状数组 || 离散化 || BZOJ 4756: [Usaco2017 Jan]Promotion Counting || Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

    题面:P3605 [USACO17JAN]Promotion Counting晋升者计数 题解:这是一道万能题,树状数组 || 主席树 || 线段树合并 || 莫队套分块 || 线段树 都可以写..记 ...

  2. 树状数组 P3605 [USACO17JAN]Promotion Counting晋升者计数

    P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 ...

  3. 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]

    题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...

  4. luogu P3605 [USACO17JAN]Promotion Counting晋升者计数

    题目链接 luogu 思路 可以说是线段树合并的练手题目吧 也没啥说的,就是dfs,然后合并... 看代码吧 错误 和写主席树错的差不多 都是变量写错.... 代码 #include <bits ...

  5. P3605 [USACO17JAN]Promotion Counting晋升者计数

    思路 线段树合并的板子.. 和子节点合并之后在值域线段树上查询即可 代码 #include <cstdio> #include <algorithm> #include < ...

  6. 洛谷 P3605 [USACO17JAN]Promotion Counting晋升者计数

    题目描述 The cows have once again tried to form a startup company, failing to remember from past experie ...

  7. Luogu3605 [USACO17JAN]Promotion Counting晋升者计数

    Luogu3605 [USACO17JAN]Promotion Counting晋升者计数 给一棵 \(n\) 个点的树,点 \(i\) 有一个权值 \(a_i\) .对于每个 \(i\) ,求 \( ...

  8. 题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】

    这道题开10倍左右一直MLE+RE,然后尝试着开了20倍就A了...窒息 对于这道题目,我们考虑使用线段树合并来做. 所谓线段树合并,就是把结构相同的线段树上的节点的信息合在一起,合并的方式比较类似左 ...

  9. [USACO17JAN]Promotion Counting 题解

    前言 巨佬说:要有线段树,结果蒟蒻打了一棵树状数组... 想想啊,奶牛都开公司当老板了,我还在这里码代码,太失败了. 话说奶牛开个公司老板不应该是FarmerJohn吗? 题解 刚看到这道题的时候竟然 ...

随机推荐

  1. web.xml常用配置详解

    web.xml常用配置详解 context-param 指定 ServletContext(上下文) 配置文件路径,基本配置一般是Spring配置文件,或者是spring-security的配置文件. ...

  2. Java容器 | 基于源码分析List集合体系

    一.容器之List集合 List集合体系应该是日常开发中最常用的API,而且通常是作为面试压轴问题(JVM.集合.并发),集合这块代码的整体设计也是融合很多编程思想,对于程序员来说具有很高的参考和借鉴 ...

  3. 黄衫女子,黄衫好.png

    正想着团队项目中数据该如何解析,就收到了来自软工课程组的一件小黄衫,真是意外之喜.详问其来源,竟是因结对项目做的"较好"而来,顿感受之有愧. 结对项目是两人对文件系统的一个小模拟, ...

  4. 25.数据结构,LinkedList ,泛型,类型通配符

    3.数据结构 数据结构是计算机存储,组织数据的方式.是指相互之间存在的一种或多种特定关系的数据元素的集合 通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率 ---------常见的数据结构 ...

  5. 数据库权限grant

    数据库权限grant 创建授权grant 权限类型(priv_type) 权限类型 代表什么? ALL 所有权限 SELECT 读取内容的权限 INSERT 插入内容的权限 UPDATE 更新内容的权 ...

  6. 云计算OpenStack核心组件---nova计算服务(7)

    一.nova介绍 Nova 是 OpenStack 最核心的服务,负责维护和管理云环境的计算资源.OpenStack 作为 IaaS 的云操作系统,虚拟机生命周期管理也就是通过 Nova 来实现的. ...

  7. make clean 和 make distclean区别-(转自秋水Leo)

    make clean仅仅是清除之前编译的可执行文件及配置文件. 而make distclean要清除所有生成的文件. Makefile 在符合GNU Makefiel惯例的Makefile中,包含了一 ...

  8. Serializable_序列化详情

     概述 Java 提供了一种对象序列化的机制.用一个字节序列可以表示一个对象,该字节序列包含该对象的数据.对象的类型和对象中存储的属性等信息.字节序列写出到文件之后,相当于文件中持久保存了一个对象的信 ...

  9. Go语言之main包

    Go语言的代码通过包(package)组织,包类似于其他语言里的库(libraries)或者模块(modules).一个包由位于单个目录下的一个或多个go源文件组成,目录定义包的作用.每个源文件都以一 ...

  10. 4. springmvc底层原理2

    Spring mvc 是子容器 Spring 是 父容器 =================================================================== pub ...