分块\(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. 通过Python将监控数据由influxdb写入到MySQL

    一.项目背景 我们知道InfluxDB是最受欢迎的时序数据库(TSDB).InfluxDB具有 持续高并发写入.无更新:数据压缩存储:低查询延时 的特点.从下面这个权威的统计图中,就可以看出Influ ...

  2. gdb调试多线程多进程

    多进程调试 我们使用gdb调试程序,gdb的调试默认是调试父进程的,如果要做到对父进程和子进程都做到调试,所以附加了调试子进程的功能. 设置条件 如果让gdb可以同时调试多个程序,只需要设置follo ...

  3. 对Linux系统内核版本稳定性测试介绍

    对Linux系统内核版本稳定性测试介绍 在对 Linux 内核版本稳定性的测试中,需要明确地声明并证明为什么版本是稳定的或者是不稳定的. 然而还没有被证明和证实当前现有的系统范围内的压力测试可以测试 ...

  4. linux 视频输出xrandr设置命令

    linux 视频输出xrandr设置命令 沈祥佑关注 0.592019.03.28 17:44:09字数 397阅读 6,886 xrandr 不带参数的xrandr 命令会列出系统支持的视频接口名称 ...

  5. openstack宿主机故障,虚拟实例恢复

    前言: 因为机房服务器运行不稳定的原因导致计算节点挂掉,然后上面的Centos7虚拟机在迁移之后开机报错.这个解决方法同样适用于其它操作系统的虚拟机.基于镜像创建的虚拟机实例. I/O error, ...

  6. libvirtd 启动成功,但却没有监听

    现象: 执行 systemctl start libvirtd在其它机器上进行测试是否监听:virsh -c qemu+tcp://host/system libvirtd启动成功,没有报错,但却没有 ...

  7. 配置文件修改java安全级别和站点信息

    配置文件修改java安全级别和站点信息原创Green_1001 最后发布于2015-04-22 23:00:09 阅读数 516 收藏展开 通过配置文件修改java安全级别 配置文件名称为deploy ...

  8. VS Code 安装后的一些配置项

    说明: 个人一直使用Notepad++作为日常文本编辑器,由于之前出现的某个原因,故决定改用VS Code. •设置中文字体 • 输入快捷键 Ctrl+Shift+P • 输入 Configure D ...

  9. Jmeter(四十六) - 从入门到精通高级篇 - Jmeter之网页图片爬虫-下篇(详解教程)

    1.简介 上一篇介绍了爬取文章,这一篇宏哥就简单的介绍一下,如何爬取图片然后保存到本地电脑中.网上很多漂亮的壁纸或者是美女.妹子,想自己收藏一些,挨个保存太费时间,那你可以利用爬虫然后批量下载. 2. ...

  10. redis 基本操作命令

    redis 基本操作 String 操作字符串 1 SET key value 设置指定 key 的值 ​ 2 GET key 获取指定 key 的值. ​ 3 GETRANGE key start ...