[九省联考2018]IIIDX

传送门
形式化题意:一棵树,对于每个节点赋予一个给定的权值,使得每个节点都不大于子树内节点,同时满足编号小的点尽可能大。
首先在所有给定的数不同的时候只要贪心一次,从小到大把数排序,之后建树在上面跑dfs,按dfn从小到大给权值。
但是这样在有相同的数据的时候是会错的。因为有可能通过交换使得子树内节点权值和根相等。(例子在luogu讨论区有的)
我们重新考虑一下。把所有的数从大到小排序,记录\(C_i\)为每个点左边还能被使用的权值个数。这样的话每次找一个位置赋权值的话,其实就是找一个位置使得它右边所有位置的\(C_i\)不大于子树大小。这个可以在线段树上二分。
不过二分出来的不一定是最好的答案,因为有重复的数字所以你取最优的结果一定是在最右侧,需要额外维护一下应该调到的位置。
然后每次更新后需要在修改位置的右边全部减去子树大小。注意,每次遍历完一个父亲的时候要加上他的子树大小。相当于把原来预留的空间放了出来。
然后就完事了……代码不长……

#include<bits/stdc++.h>
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
#define enter putchar('\n')
#define pr pair<int,int>
#define mp make_pair
#define fi first
#define sc second
using namespace std;
typedef long long ll;
const int M = 1000005;
const int N = 10000005;

int read()
{
   int ans = 0,op = 1;char ch = getchar();
   while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
   while(ch >='0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
   return ans * op;
}

struct tree
{
   int minn,lazy;
}t[M<<2];
bool cmp(int x,int y){return x > y;}
int n,a[M],b[M],cur = 1,dfn[M],idx,fa[M],size[M],ans[M],cnt[M];
double k;

void pushdown(int p)
{
   t[p<<1].lazy += t[p].lazy,t[p<<1|1].lazy += t[p].lazy;
   t[p<<1].minn += t[p].lazy,t[p<<1|1].minn += t[p].lazy;
   t[p].lazy = 0;
}

void build(int p,int l,int r)
{
   if(l == r) {t[p].minn = l;return;}
   int mid = (l+r) >> 1;
   build(p<<1,l,mid),build(p<<1|1,mid+1,r);
   t[p].minn = min(t[p<<1].minn,t[p<<1|1].minn);
}

int query(int p,int l,int r,int k)
{
   if(l == r){return t[p].minn >= k ? l : l + 1;}
   pushdown(p);
   int mid = (l+r) >> 1;
   if(k <= t[p<<1|1].minn) return query(p<<1,l,mid,k);
   else return query(p<<1|1,mid+1,r,k);
}

void update(int p,int l,int r,int kl,int kr,int val)
{
   if(l == kl && r == kr) {t[p].minn += val,t[p].lazy += val;return;}
   pushdown(p);
   int mid = (l+r) >> 1;
   if(kr <= mid) update(p<<1,l,mid,kl,kr,val);
   else if(kl > mid) update(p<<1|1,mid+1,r,kl,kr,val);
   else update(p<<1,l,mid,kl,mid,val),update(p<<1|1,mid+1,r,mid+1,kr,val);
   t[p].minn = min(t[p<<1].minn,t[p<<1|1].minn);
}

int main()
{
   n = read(),scanf("%lf",&k);
   rep(i,1,n) a[i] = read();
   sort(a+1,a+1+n,cmp);
   per(i,n-1,1) cnt[i] = (a[i] == a[i+1]) ? cnt[i+1] + 1 : 0;
   rep(i,1,n) fa[i] = (int)floor(i/k),size[i] = 1;
   per(i,n,1) size[fa[i]] += size[i];
   build(1,1,n);
   rep(i,1,n)
   {
      if(fa[i] && fa[i] != fa[i-1]) update(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
      int cur = query(1,1,n,size[i]);
      cur += cnt[cur],cnt[cur]++,ans[i] = cur;
      update(1,1,n,cur,n,-size[i]);
   }
   rep(i,1,n) printf("%d ",a[ans[i]]);enter;
   return 0;
}

原文地址:https://www.cnblogs.com/captain1/p/10508179.html

时间: 2024-08-17 11:22:30

[九省联考2018]IIIDX的相关文章

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

P4364 [九省联考2018]IIIDX 题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了. 这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻.同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高. 题目描述 这一天,Konano 接到了一个任务

[九省联考 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!

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

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

[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+

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

Description 题库链接 给出一个 \(n\times m\) 的棋盘,棋盘的每个格子有两个权值 \(A,B\) . Alice 和 Bob 轮流操作在棋盘上放棋子,一个格子能放棋子的前提条件是这个格子的左侧和上侧均放了棋子.对于 Alice 放棋子的格子,能获得该格子的 \(A\) 的权值:对于 Bob 放棋子的格子,能获得该格子的 \(B\) 的权值. Alice 想最大化得分差, Bob 想最小化得分差,求最后得分差. \(1\leq n,m\leq 10\) Solution 比

[九省联考2018]林克卡特树(DP+wqs二分)

对于k=0和k=1的点,可以直接求树的直径. 然后对于60分,有一个重要的转化:就是求在树中找出k+1条点不相交的链后的最大连续边权和. 这个DP就好.$O(nk^2)$ 然后我们完全不可以想到,将best[k](选择k条链的答案)打表输出,更不可能然后作差分,发现得到的数组是递减的. 这说明:best[k]是一个上凸包. 于是我们可以二分一个斜率去切这个凸包(类似导数),根据切点横坐标与k的大小旋转直线(改变斜率). 考虑给你一个直线斜率k,怎么找到它和凸包的切点.实际上就相当于将这个凸函数减