分块\(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. 1、requests基础

    一.升级pip版本的命令 : python -m pip install --upgrade pip 二.requests安装  windows系统系cmd运行 pip install request ...

  2. 什么是 Mock 测试?

    什么是 Mock? 作为动词,Mock 是模拟.模仿的意思. 作为名词,Mock 是能够模仿真实对象行为的模拟对象. 那么,在软件测试中,Mock 所模拟的对象是什么呢? 模拟的是 SUT(Syste ...

  3. springboot 项目中css js 等静态资源无法访问的问题

    目录 问题场景 问题分析 问题解决 问题场景 今天在开发一个springboot 项目的时候突然发现 css js 等静态资源竟然都报404找不到,折腾了好久终于把问题都解决了,决定写篇博客,纪录总结 ...

  4. Educational Codeforces Round 101 (Rated for Div. 2)

    A. Regular Bracket Sequence 题意:题目中给(和)还有?,其中?可以转换成为()中的任何一个,并且所给样例中只出现一次(),问能不能括号匹配 思路:直接看第一个和最后一个能不 ...

  5. mac SSH私钥取消密码(passphrase)

    取消私钥中的密码: 1.使用openssl命令去掉私钥的密码openssl rsa -in ~/.ssh/id_rsa -out ~/.ssh/id_rsa_new 2.备份旧私钥mv ~/.ssh/ ...

  6. fail to start File System Check

    fail to start File System Check 方法A: 输入root的密码 cd /etc 1 vim /etc/fstab 将所有分区最后的数字 1和2 全都改为0 reboot ...

  7. python类传参示例

    1 class f(): 2 3 def __init__(self, *args, **kwargs): 4 print('args Is', args) # args Is ('5', 'fff' ...

  8. BogoMips 和cpu主频无关 不等于cpu频率

    http://tinylab.org/explore-linux-bogomips/ 内核探索:Linux BogoMips 探秘 Tao HongLiang 创作于 2015/05/12 打赏 By ...

  9. Java反射机制详情(2)

    | |目录 运行环境 Java语言的反射机制 Class中的常用方法(获得类的构造方法) Class中的常用方法(获得类的属性) Class中的常用方法(获得类的方法) 反射动态调用类的成员 1.运行 ...

  10. Linux C 文件IO

    文件IO 2021-05-31 12:46:14 星期一 目录 文件IO 基础IO open 错误 creat read 一个例子 write close lseek 文件空洞 unlink删除 io ...