[luogu] P4364 [九省联考2018]IIIDX(贪心)

P4364 [九省联考2018]IIIDX

题目背景

Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了。

这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。

题目描述

这一天,Konano 接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目 的解锁顺序。游戏内共有n 首曲目,每首曲目都会有一个难度d,游戏内第i 首曲目会在玩家Pass 第\(\lfloor \frac{i}{k} \rfloor\) 首曲目后解锁(\(\lfloor x \rfloor\) 为下取整符号)若\(\lfloor \frac{i}{k} \rfloor = 0\),则说明这首曲目无需解锁

举个例子:当k = 2 时,第1 首曲目是无需解锁的(\(\lfloor \frac{1}{2} \rfloor = 0\)),第7 首曲目需要玩家Pass 第\(\lfloor \frac{7}{2} \rfloor = 3\) 首曲目才会被解锁。

Konano 的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i 满足

\(d_i \geq d_{\lfloor \frac{i}{k} \rfloor}\)

当然这难不倒曾经在信息学竞赛摸鱼许久的Konano。那假如是你,你会怎么解决这份任务呢?

输入输出格式

输入格式:

第1 行1 个正整数n 和1 个小数k,n 表示曲目数量,k 其含义如题所示。

第2 行n 个用空格隔开的正整数d,表示这n 首曲目的难度。

输出格式:

输出到文件iiidx.out 中。

输出1 行n 个整数,按顺序输出安排完曲目顺序后第i 首曲目的难度。

若有多解,则输出d1 最大的;若仍有多解,则输出d2最大的,以此类推。

输入输出样例

输入样例#1: 复制

4 2.0
114 514 1919 810

输出样例#1: 复制

114 810 514 1919

说明

题解

做个人不好吗。。

本题有一个55分的贪心,可以过60分
就是直接按题意暴力建树然后贪心,能赋大值就赋大值。
树的边就是按\(\lfloor \frac{i}{k} \rfloor\)来的。

事实上这个贪心是不能解决多个\(d_i\)相同的。

input
4 2.0
1 1 1 2
output
1 1 2 1
错误答案
1 1 1 2

即在建树过程中,(图片来自NaVi_Awson)

当2节点和4节点可以取相同值,且3独立时,我们应该是优先,2,4为1,3为2的,但是贪心一定会把2赋值给4节点。

我们维护一个数组\(f[i]\),表示\(i\)节点之前还有剩余的点数。
从大到小排序,然后我们找优先靠左的满足\(f[i]>=size[x]\)的数字。
\(size[x]\)表示答案序列第\(x\)位的子树大小。
然后维护一个\(nxt[]\)数组,表示相同的数值时优先选靠右的。
然后第\(i\)到\(n\)位的\(f[]-size[x]\);
这样可以解决掉那个重复的带来的影响。
两个循环暴力找和减去是\(O(n^2)\)
转移到线段树上就可以了\(O(nlogn)\)

我之前一直不明白为什么如果当前点有父亲,要重新加上父亲的预留值。

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int sum[N<<2],lazy[N<<2];
int a[N],n,ans[N],size[N],fa[N],nxt[N];
double k;
int read(){
    int x=0,w=1;char ch=getchar();
    while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*w;
}

void build(int root,int left,int right){
    int mid=(left+right)>>1;
    if(left==right){
        sum[root]=left;return ;
    }build(root<<1,left,mid);build(root<<1|1,mid+1,right);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

void push(int root,int left,int right){
    sum[root<<1]+=lazy[root];sum[root<<1|1]+=lazy[root];
    lazy[root<<1]+=lazy[root];lazy[root<<1|1]+=lazy[root];
    lazy[root]=0;
}

void update(int root,int left,int right,int l,int r,int v){
    if(left>r||right<l)return ;
    if(lazy[root])push(root,left,right);
    if(left>=l&&right<=r){
        sum[root]+=v;lazy[root]+=v;
        return ;
    }int mid=(left+right)>>1;
    if(mid>=l) update(root<<1,left,mid,l,r,v);
    if(mid<r)  update(root<<1|1,mid+1,right,l,r,v);
    sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

int query(int root,int left,int right,int k){
    if(left==right){
        return sum[root]>=k?left:left+1;
    }int mid=(left+right)>>1;
    if(lazy[root])push(root,left,right);
    if(sum[root<<1|1]>=k) return query(root<<1,left,mid,k);
    else return query(root<<1|1,mid+1,right,k);

}

bool cmp(int a,int b){return a>b;}

int main(){
    n=read();cin>>k;
    for(int i=1;i<=n;i++)a[i]=read();sort(a+1,a+n+1,cmp);
    for(int i=n;i>=1;i--){
        nxt[i]=i;fa[i]=(1.0*i/k);
        size[i]++;size[fa[i]]+=size[i];
        if(a[i]==a[i+1])nxt[i]=nxt[i+1];
    }build(1,1,n);
    for(int i=1;i<=n;i++){
        if(fa[i]&&fa[i]!=fa[i-1])update(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
        int tmp=nxt[query(1,1,n,size[i])];ans[i]=tmp;
        update(1,1,n,tmp,n,-size[i]);
    }
    for(int i=1;i<=n;i++)cout<<a[ans[i]]<<' ';
    return 0;
}

P4364 [九省联考2018]IIIDX
题目背景
Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了。

这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。

题目描述
这一天,Konano 接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目 的解锁顺序。游戏内共有n 首曲目,每首曲目都会有一个难度d,游戏内第i 首曲目会在玩家Pass 第\lfloor \frac{i}{k} \rfloor?
k
i
?
? 首曲目后解锁(\lfloor x \rfloor?x? 为下取整符号)若\lfloor \frac{i}{k} \rfloor = 0?
k
i
?
?=0,则说明这首曲目无需解锁

举个例子:当k = 2 时,第1 首曲目是无需解锁的(\lfloor \frac{1}{2} \rfloor = 0?
2
1
?
?=0),第7 首曲目需要玩家Pass 第\lfloor \frac{7}{2} \rfloor = 3?
2
7
?
?=3 首曲目才会被解锁。

Konano 的工作,便是安排这些曲目的顺序,使得每次解锁出的曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i 满足

d_i \geq d_{\lfloor \frac{i}{k} \rfloor}d
i
?
≥d
?
k
i
?
?
?

当然这难不倒曾经在信息学竞赛摸鱼许久的Konano。那假如是你,你会怎么解决这份任务呢?

输入输出格式
输入格式:

第1 行1 个正整数n 和1 个小数k,n 表示曲目数量,k 其含义如题所示。

第2 行n 个用空格隔开的正整数d,表示这n 首曲目的难度。

输出格式:

输出到文件iiidx.out 中。

输出1 行n 个整数,按顺序输出安排完曲目顺序后第i 首曲目的难度。

若有多解,则输出d1 最大的;若仍有多解,则输出d2最大的,以此类推。

输入输出样例
输入样例#1: 复制

4 2.0
114 514 1919 810

输出样例#1: 复制

114 810 514 1919

说明

题解
做个人不好吗。。

本题有一个55分的贪心,可以过60分
就是直接按题意暴力建树然后贪心,能赋大值就赋大值。
树的边就是按\lfloor \frac{i}{k} \rfloor?
k
i
?
?来的。

事实上这个贪心是不能解决多个d_id
i
?
相同的。

input
4 2.0
1 1 1 2
output
1 1 2 1
错误答案
1 1 1 2
即在建树过程中,(图片来自NaVi_Awson)

当2节点和4节点可以取相同值,且3独立时,我们应该是优先,2,4为1,3为2的,但是贪心一定会把2赋值给4节点。

我们维护一个数组f[i]f[i],表示ii节点之前还有剩余的点数。
从大到小排序,然后我们找优先靠左的满足f[i]>=size[x]f[i]>=size[x]的数字。
size[x]size[x]表示答案序列第xx位的子树大小。
然后维护一个nxt[]nxt[]数组,表示相同的数值时优先选靠右的。
然后第ii到nn位的f[]-size[x]f[]?size[x];
这样可以解决掉那个重复的带来的影响。
两个循环暴力找和减去是O(n^2)O(n
2
)
转移到线段树上就可以了O(nlogn)O(nlogn)

我之前一直不明白为什么如果当前点有父亲,要重新加上父亲的预留值。

Code

include

include

include

include

include

using namespace std;
const int N=1e6+5;
int sum[N<<2],lazy[N<<2];
int a[N],n,ans[N],size[N],fa[N],nxt[N];
double k;
int read(){
int x=0,w=1;char ch=getchar();
while(ch>‘9‘||ch<‘0‘){if(ch==‘-‘)w=-1;ch=getchar();}
while(ch>=‘0‘&&ch<=‘9‘)x=x10+ch-‘0‘,ch=getchar();
return x
w;
}

void build(int root,int left,int right){
int mid=(left+right)>>1;
if(left==right){
sum[root]=left;return ;
}build(root<<1,left,mid);build(root<<1|1,mid+1,right);
sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

void push(int root,int left,int right){
sum[root<<1]+=lazy[root];sum[root<<1|1]+=lazy[root];
lazy[root<<1]+=lazy[root];lazy[root<<1|1]+=lazy[root];
lazy[root]=0;
}

void update(int root,int left,int right,int l,int r,int v){
if(left>r||right<l)return ;
if(lazy[root])push(root,left,right);
if(left>=l&&right<=r){
sum[root]+=v;lazy[root]+=v;
return ;
}int mid=(left+right)>>1;
if(mid>=l) update(root<<1,left,mid,l,r,v);
if(mid<r) update(root<<1|1,mid+1,right,l,r,v);
sum[root]=min(sum[root<<1],sum[root<<1|1]);
}

int query(int root,int left,int right,int k){
if(left==right){
return sum[root]>=k?left:left+1;
}int mid=(left+right)>>1;
if(lazy[root])push(root,left,right);
if(sum[root<<1|1]>=k) return query(root<<1,left,mid,k);
else return query(root<<1|1,mid+1,right,k);

}

bool cmp(int a,int b){return a>b;}

int main(){
n=read();cin>>k;
for(int i=1;i<=n;i++)a[i]=read();sort(a+1,a+n+1,cmp);
for(int i=n;i>=1;i--){
nxt[i]=i;fa[i]=(1.0*i/k);
size[i]++;size[fa[i]]+=size[i];
if(a[i]==a[i+1])nxt[i]=nxt[i+1];
}build(1,1,n);
for(int i=1;i<=n;i++){
if(fa[i]&&fa[i]!=fa[i-1])update(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
int tmp=nxt[query(1,1,n,size[i])];ans[i]=tmp;
update(1,1,n,tmp,n,-size[i]);
}
for(int i=1;i<=n;i++)cout<<a[ans[i]]<<‘ ‘;
return 0;
}
Markdown selection 21 bytes 2 words 0 lines Ln 1, Col 23 HTML 3224 characters 246 words 115 paragraphs

原文地址:https://www.cnblogs.com/hhh1109/p/10667185.html

时间: 2024-11-06 08:16:50

[luogu] P4364 [九省联考2018]IIIDX(贪心)的相关文章

[九省联考2018]IIIDX

传送门 形式化题意:一棵树,对于每个节点赋予一个给定的权值,使得每个节点都不大于子树内节点,同时满足编号小的点尽可能大. 首先在所有给定的数不同的时候只要贪心一次,从小到大把数排序,之后建树在上面跑dfs,按dfn从小到大给权值. 但是这样在有相同的数据的时候是会错的.因为有可能通过交换使得子树内节点权值和根相等.(例子在luogu讨论区有的) 我们重新考虑一下.把所有的数从大到小排序,记录\(C_i\)为每个点左边还能被使用的权值个数.这样的话每次找一个位置赋权值的话,其实就是找一个位置使得它

luogu P4365 [九省联考2018]秘密袭击coat

luogu 这里不妨考虑每个点的贡献,即求出每个点在多少个联通块中为第\(k\)大的(这里权值相同的可以按任意顺序排大小),然后答案为所有点权值\(*\)上面求的东西之和 把比这个点大的点看成\(1\),小于等于他的看成\(0\),那么就是要求出包含枚举的那个点并且权值和为\(k-1\)的联通块个数,可以树型\(dp\),设\(f_{x,j}\)表示联通块最上面的点为\(x\)并且权值和为\(j\)的联通块数,转移树型背包即可,具体细节见代码.复杂度可以做到\(O(nk)\) 所以总复杂度为\(

[九省联考 2018]IIIDX

Description 题库链接 给你 \(n+1\) 个节点的一棵树,节点编号为 \(0\sim n\) , \(0\) 为根.边集为 \(\mathbb{E}=\left\{(u,v)\big|\forall i\in[1,n],\left(\left\lfloor\frac{i}{k}\right\rfloor,i\right)\right\}\) .给出 \(n\) 个待选序号,让你为 \(1\sim n\) 这 \(n\) 个节点编号,第 \(i\) 号节点编为 \(a_i\),要求父

解题:九省联考2018 IIIDX

题面 我当时在考场上划水的时候好像乱搞搞了20pts,然后发现一堆同届的都写了55pts的贪心=.=??? 那就先说那55pts的贪心吧,这个现在看起来还是非常显然的,就是按题意来每一块是分属一个点的,其实这就是棵树,排序之后从叶子往上递增地放就可以了,挺送的=.= 为什么错了,显然有相同的数的时候可能把一个大点的数放前面也是对的,然后就不优了 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4

【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)

[BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且 这个格子的左侧及上方的所有格子内都有棋子. 棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij.Bij.在 游戏结束后,菲菲和牛牛会分别计算自己

九省联考 2018 游记

Day0:乘火车到了上海.明天就是激动人心的比赛啦 深夜和室友看<我在七年后等你>.这真是一款不错的手游,让人印象深刻啊 Day1:迷迷糊糊到了学校.编程环境是Win7?不太习惯啊. T1:一眼状压dp题. T2:肯定可以建成一棵树,然后直接贪心?不对啊,T2不应该这么水啊(开始怀疑) T3:乍一看怎么一点思路没有啊. 8:40~11:10:持续思考T3中. 11:10:终于有思路了!如果直接NTT向上dp的话,因为链的情况复杂度会不对,所以似乎可以树剖!用线段树分治和NTT处理重链上的dp!

[loj 2478][luogu P4843]「九省联考 2018」林克卡特树

传送门 Description 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做"LCT" 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益:若vi < 0 ,则表示走这条边需要支付- vi 的过路费.小L 需要控制主角Link 切掉(Cut)树上的 恰好K

[BZOJ5248][九省联考2018]双木棋chess

bzoj luogu sol 首先,要保证一个格子的左边和上方都放满了棋子,就需要这个点的左上方那个矩形都放满了棋子. 这样放旗子状态就会是一个自左下至右上的轮廓线. 状态数? 跟\(yyb,ppl\)讨论了一下状态数理论上应该是\(C_{20}^{10}\)啊. 然而... #include<cstdio> #include<algorithm> using namespace std; int n,m,a[20],tot; void dfs(int u) { if (u==n+

[BZOJ5251][九省联考2018]劈配(网络流)

5251: [2018多省省队联测]劈配 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 33  Solved: 22[Submit][Status][Discuss] Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayid顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的: 总共n名参赛选手(编号