题解 P3605 [USACO17JAN]Promotion Counting P
分块\(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的最后一个下属)
分为以下两种情况:
已经是最后一个,没有下属,那么\(head[x]\)一定是0,此时直接\(return\),并且给\(tail[x]\)赋值
有子节点,此时用一个\(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的更多相关文章
- 线段树合并 || 树状数组 || 离散化 || BZOJ 4756: [Usaco2017 Jan]Promotion Counting || Luogu P3605 [USACO17JAN]Promotion Counting晋升者计数
题面:P3605 [USACO17JAN]Promotion Counting晋升者计数 题解:这是一道万能题,树状数组 || 主席树 || 线段树合并 || 莫队套分块 || 线段树 都可以写..记 ...
- 树状数组 P3605 [USACO17JAN]Promotion Counting晋升者计数
P3605 [USACO17JAN]Promotion Counting晋升者计数 题目描述 奶牛们又一次试图创建一家创业公司,还是没有从过去的经验中吸取教训--牛是可怕的管理者! 为了方便,把奶牛从 ...
- 洛谷P3605 [USACO17JAN] Promotion Counting 晋升者计数 [线段树合并]
题目传送门 Promotion Counting 题目描述 The cows have once again tried to form a startup company, failing to r ...
- luogu P3605 [USACO17JAN]Promotion Counting晋升者计数
题目链接 luogu 思路 可以说是线段树合并的练手题目吧 也没啥说的,就是dfs,然后合并... 看代码吧 错误 和写主席树错的差不多 都是变量写错.... 代码 #include <bits ...
- P3605 [USACO17JAN]Promotion Counting晋升者计数
思路 线段树合并的板子.. 和子节点合并之后在值域线段树上查询即可 代码 #include <cstdio> #include <algorithm> #include < ...
- 洛谷 P3605 [USACO17JAN]Promotion Counting晋升者计数
题目描述 The cows have once again tried to form a startup company, failing to remember from past experie ...
- Luogu3605 [USACO17JAN]Promotion Counting晋升者计数
Luogu3605 [USACO17JAN]Promotion Counting晋升者计数 给一棵 \(n\) 个点的树,点 \(i\) 有一个权值 \(a_i\) .对于每个 \(i\) ,求 \( ...
- 题解 P3605 【[USACO17JAN]Promotion Counting晋升者计数】
这道题开10倍左右一直MLE+RE,然后尝试着开了20倍就A了...窒息 对于这道题目,我们考虑使用线段树合并来做. 所谓线段树合并,就是把结构相同的线段树上的节点的信息合在一起,合并的方式比较类似左 ...
- [USACO17JAN]Promotion Counting 题解
前言 巨佬说:要有线段树,结果蒟蒻打了一棵树状数组... 想想啊,奶牛都开公司当老板了,我还在这里码代码,太失败了. 话说奶牛开个公司老板不应该是FarmerJohn吗? 题解 刚看到这道题的时候竟然 ...
随机推荐
- 【目录】Java项目开发中的知识记录
此篇文章为学习Java的目录,<a href="#"></>这种的是还没有写的文章.已经加a标签的是已经写完的.没写的文章急切需要的话可以直接留言,不是特别 ...
- springboot国际化与@valid国际化支持
springboot国际化 springboot对国际化的支持还是很好的,要实现国际化还简单.主要流程是通过配置springboot的LocaleResolver解析器,当请求打到springboot ...
- 改善c++程序的150个建议(读后总结)-------10-11
10. 优化结构体中元素的布局 结构体变量所占空间大小并不是其所含类型所占字节数之和,其所占内存字节数涉及到字节对齐. 字节对齐 :变量在内存中储存都是以字节数为单位,每一个字节都有自己的地址,逻辑上 ...
- 30.Stream流
5.Stream流 5.1体验Stream流[理解] 案例需求 按照下面的要求完成集合的创建和遍历 创建一个集合,存储多个字符串元素 把集合中所有以"张"开头的元素存储到一个新的集 ...
- istio sidecar流量处理机制及配置
sidecar 介绍 在istio的流量管理等功能,都需要通过下发的配置应用到应用运行环境执行后生效,负责执行配置规则的组件在service mesh中承载应用代理的实体被称为side-car Ist ...
- 安装SpecCPU2006 on Linux of CentOS6.3, gcc4.4.7
安装SpecCPU2006 on Linux of CentOS6.3, gcc4.4.7 由于在tools/bin目录中只有ia64-linux,所以在直接运行./install.sh脚本时,系统会 ...
- 创建用户(adduser和useradd)和删除用户(userdel)
创建用户(adduser和useradd)和删除用户(userdel)及 一 用户创建命令: # adduser 用户名 # useradd 用户名 1) useradd 与 adduser ...
- STM32——EEPROM使用——(转载)
一.I2C接口读写EEPROM(AT24C02) --主模式,分别用作主发送器和主接收器.通过查询事件的方式来确保正常通信. 1.I 2C接口初始化 与其他对GPIO 复用的外设一样,它先调用了用户函 ...
- MyBatis 全局配置文件详解(七)
MyBatis 配置文件作用 MyBatis配置文件包含影响 MyBatis 框架正常使用的功能设置和属性信息.它的作用好比手机里的设置图标,点击这个图标就可以帮助我们查看手机的属性信息和设置功能.其 ...
- [Django高级之Auth模块]
[Django高级之Auth模块] auth模块 ←详情点击查看 1.Auth模块是什么 Auth模块是Django自带的用户认证模块: 我们在开发一个网站的时候,无可避免的需要设计实现网站的用户系统 ...