[十二省联考2019]D2T2春节十二响

嘟嘟嘟


这题真没想到这么简单……


首先有60分大礼:\(O(n ^ 2logn)\)贪心。(我也不知道为啥就是对的)
然后又送15分链:维护两个堆,每次去堆顶的最大值。


这时候得到75分已经很开心了,但其实离AC也就差一点点。
链的做法已经给了我们提示:合并两个堆。其实这就相当于二叉树。那多叉树呢?就合并多个堆呗!从子树向上递归的时候不断将子树启发式合并,复杂度就保证了。


代码真的很短

#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
inline ll read()
{
  ll ans = 0;
  char ch = getchar(), last = ' ';
  while(!isdigit(ch)) last = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(last == '-') ans = -ans;
  return ans;
}
inline void write(ll x)
{
  if(x < 0) x = -x, putchar('-');
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}

int n, a[maxn];
struct Edge
{
  int nxt, to;
}e[maxn];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
  e[++ecnt] = (Edge){head[x], y};
  head[x] = ecnt;
}

priority_queue<int> q[maxn];
int siz[maxn], tp[maxn];
In int dfs(int now)
{
  int id1 = now;
  for(int i = head[now], v; ~i; i = e[i].nxt)
    {
      v = e[i].to;
      int id2 = dfs(v);
      if(q[id1].size() < q[id2].size()) swap(id1, id2);
      int Siz = q[id2].size();
      for(int j = 1; j <= Siz; ++j) tp[j] = q[id1].top(), q[id1].pop();
      for(int j = 1; j <= Siz; ++j)
    {
      q[id1].push(max(tp[j], q[id2].top()));
      q[id2].pop();
    }
    }
  q[id1].push(a[now]);
  return id1;
}

int main()
{
  Mem(head, -1);
  n = read();
  for(int i = 1; i <= n; ++i) a[i] = read();
  for(int i = 2, x; i <= n; ++i) x = read(), addEdge(x, i);
  int id = dfs(1);
  ll ans = 0;
  while(!q[id].empty()) ans += q[id].top(), q[id].pop();
  write(ans), enter;
  return 0;
}



这题我考场上写了75分,结果成绩一出只剩20分了,当时真的怀疑人生,因为我对自己的暴力有十足的信心。然后查代码的时候看到了触目惊心的一幕:

if(judge_line) {work1(); return 0;}

天知道我judge_line()后面的括号去哪了。
-Wall没给我报错,自己复查的时候也没发现,编译也能过,就这样白白没了55分。
考场代码全放上来吧

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<cctype>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 2e5 + 5;
const int N = 18;
In ll read()
{
  ll ans = 0;
  char ch = getchar(), las = ' ';
  while(!isdigit(ch)) las = ch, ch = getchar();
  while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
  if(las == '-') ans = -ans;
  return ans;
}
In void write(ll x)
{
  if(x < 0) putchar('-'), x = -x;
  if(x >= 10) write(x / 10);
  putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
  freopen("spring.in", "r", stdin);
  freopen("spring.out", "w", stdout);
#endif
}

int n, a[maxn];
struct Edge
{
  int nxt, to;
}e[maxn];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
  e[++ecnt] = (Edge){head[x], y};
  head[x] = ecnt;
}

int fa[N + 2][maxn], dep[maxn], siz[maxn];
In void dfs(int now, int _f)
{
  siz[now] = 1;
  for(int i = 1; (1 << i) <= dep[now]; ++i)
    fa[i][now] = fa[i - 1][fa[i - 1][now]];
  for(int i = head[now], v; ~i; i = e[i].nxt)
    {
      if((v = e[i].to) == _f) continue;
      dep[v] = dep[now] + 1, fa[0][v] = now;
      dfs(v, now);
      siz[now] += siz[v];
    }
}
In int lca(int x, int y)
{
  if(dep[x] < dep[y]) swap(x, y);
  for(int i = N; i >= 0; --i)
    if(dep[x] - (1 << i) >= dep[y]) x = fa[i][x];
  if(x == y) return x;
  for(int i = N; i >= 0; --i)
    if(fa[i][x] ^ fa[i][y]) x = fa[i][x], y = fa[i][y];
  return fa[0][x];
}

struct Node
{
  int val, siz, id;
  In bool operator < (const Node& oth)const
  {
    return val > oth.val || (val == oth.val && siz < oth.siz);
  }
}t[maxn];
vector<int> vec[maxn];
int cnt = 0;
ll ans = 0;
In void work0()
{
  for(int i = 1; i <= n; ++i) t[i] = (Node){a[i], siz[i], i};
  sort(t + 1, t + n + 1);
  for(int i = 1; i <= n; ++i)
    {
      bool flg1 = 0;
      for(int j = 1; j <= cnt && !flg1; ++j)
    {
      bool flg2 = 1;
      for(int k = 0; k < (int)vec[j].size() && flg2; ++k)
        {
          int v = vec[j][k], z = lca(t[i].id, v);
          if(z == v || z == t[i].id) flg2 = 0;
        }
      if(flg2) vec[j].push_back(t[i].id), flg1 = 1;
    }
      if(!flg1) vec[++cnt].push_back(t[i].id), ans += t[i].val;
    }
  write(ans), enter;
}

int du[maxn];
In bool judge_line()
{
  for(int i = 1; i <= n; ++i) if(du[i] > 2) return 0;
  return 1;
}
priority_queue<int> q[3];
In void dfs1(int now, int _f, int pos)
{
  q[pos].push(a[now]);
  for(int i = head[now]; ~i; i = e[i].nxt)
    dfs1(e[i].to, now, pos);
}
In void work1()
{
  for(int i = head[1], j = 1; ~i; i = e[i].nxt, ++j)
    dfs1(e[i].to, 1, j);
  while(!q[1].empty() || !q[2].empty())
    {
      if(q[2].empty()) {ans += q[1].top(), q[1].pop(); continue;}
      if(q[1].empty()) {ans += q[2].top(), q[2].pop(); continue;}
      if(q[1].top() >= q[2].top()) ans += q[1].top();
      else ans += q[2].top();
      q[1].pop(), q[2].pop();
    }
  write(ans + a[1]), enter;
}

int main()
{
  MYFILE();
  Mem(head, -1);
  n = read();
  for(int i = 1; i <= n; ++i) a[i] = read();
  for(int i = 2; i <= n; ++i)
    {
      int x = read();
      addEdge(x, i);
      ++du[x], ++du[i];
    }
  if(judge_line) {work1(); return 0;}
  dfs(1, 0);
  if(n <= 2000) {work0(); return 0;}
  work0();
  return 0;
}
/*
8
1 5 2 3 4 1 4 2
1 2 1 3 4 5 7
 */

原文地址:https://www.cnblogs.com/mrclr/p/10786117.html

时间: 2024-10-01 06:28:47

[十二省联考2019]D2T2春节十二响的相关文章

「十二省联考 2019」字符串问题

「十二省联考 2019」字符串问题 解题思路 傻逼题.. 考虑问题转化为一个A串向其支配的所有B串的后缀A串连边,如果有环答案 \(-1\) 否则是这个 \(\text{DAG}\) 上最长路径,直接建图是 \(n^2\) 的,考虑优化建图即可. 由于 \(A,B\) 都是原串的一个子串,那么对原串的反串建 SAM,一个子串的后缀就是其所在节点上比它长的串以及,其子树里的所有串. 首先将所有 \(A,B\) 串在 SAM上用倍增定位并新建节点,把SAM上每个节点拆成入点和出点,对于SAM每一个节

「十二省联考 2019」字符串问题 解题报告

「十二省联考 2019」字符串问题 当场就去世了,我这菜人改了一下午 考虑一个A,B之间的连边实际表示了两个A之间的有向边,然后把A的连边处理好,就转成了拓扑排序找环+最长链 但是边数很多,考虑优化连边 A,B之间的连边显然没法优化的,考虑一个B可以表示所有它的后缀A 把串反向建出SAM,然后一个B的后缀就是par树的子树 可以拿倍增定位 好了这题就没了 注意到一个事情,定位的点可能重复,于是对SAM拆点,每个点挂一个vector表示一个A或者B的点在SAM的这个位置 然后考虑如何连边 一个B所

[十二省联考2019]异或粽子(可持久化tire,堆)

[十二省联考2019]异或粽子(luogu) Description 题目描述 小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 nn 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 11 到 nn.第 ii 种馅儿具有一个非负整数的属性值 a_iai?.每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子.小粽准备用这些馅儿来做出 kk 个粽子. 小粽的做法是:选两个整数数 ll, rr,满足 1 \leqslant l \leqslant r

[十二省联考2019]春节十二响

好后悔考场没思考这道最良心的水题...t1花了我两个半点结果连20都拿不到 哭了 题目描述:略 思路:将一个点下面的几条链合并成一条 用长链剖分的话似乎可以简化操作 实在太水了所以没啥好说的... 1 #include<cstdio> 2 #include<queue> 3 using std::priority_queue; 4 typedef long long lint; 5 const int N=200011; 6 template<typename st>

P5290 [十二省联考2019]春节十二响

题目链接 题意分析 首先考虑链的话 就是将\(1\)部分的两条子链排序之后 贪心合并即可 那么考虑树的话 我们照样合并就行了 首先 排序的话 我们使用堆就可以了 然后 涉及到了两点问题 \(1.\)我们对于\(u\)以及\(v\)这两个维护好的堆合并的话 为了保证时间复杂度 我们需要使用启发式合并 \(2.\)我们由于启发式合并的话涉及到了交换两个堆的问题 听巨佬说 c++11的话 直接swap是\(O(1)\)的 否则的话 直接\(swap\)就是\(O(n)\) 所以我们考虑维护新的编号 然

#4349. 「十二省联考 2019」异或粽子

题意内存限制:1024 MiB时间限制:1500 ms小粽是一个喜欢吃粽子的好孩子.今天她在家里自己做起了粽子. 小粽面前有 $n$ 种互不相同的粽子馅儿,小粽将它们摆放为了一排,并从左至右编号为 $1$ 到 $n$.第 $i$ 种馅儿具有一个非负整数的属性值 $a_i$.每种馅儿的数量都足够多,即小粽不会因为缺少原料而做不出想要的粽子.小粽准备用这些馅儿来做出 $k$ 个粽子. 小粽的做法是:选两个整数数 $l,r$,满足 $1\le l\le r\le n$,将编号在 $[l,r]$ 范围内

[十二省联考2019]异或粽子 (可持久化01tire 堆)

/* 查询异或最大值的方法是前缀和一下, 在01trie上二分 那么我们可以对于n个位置每个地方先求出最大的数, 然后把n个信息扔到堆里, 当我们拿出某个位置的信息时, 将他去除当前最大后最大的信息插入到堆中 所以动态维护01trie就可以了 */ #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> #define mm

十二省联考 2019

T1 给一个序列,求前 $k$ 大区间异或和的和 $n \leq 500000,k \leq min(n^2,200000)$ sol: 超级钢琴 对每个 $i$,维护一个三元组 $(l,r,i)$ 表示左端点在 $[l,r]$,右端点在 $i$ 的区间异或最值,维护一个堆,按这个异或最值排序,每次将堆顶拿出来,分裂成最多两个区间,查一下异或最大值即可,区间异或最大值可以前缀和+可持久化 Trie 来解决 #include <bits/stdc++.h> #define LL long lon

[十二省联考2019] 异或粽子 解题报告 (可持久化Trie+堆)

interlinkage: https://www.luogu.org/problemnew/show/P5283 description: solution: 显然有$O(n^2)$的做法,前缀和优化一下即可 正解做法是先确定一个右端点$r$,找到最优的$l$使得该区间的异或和最大,这个可以用可持久化$Trie$实现.不懂的话可以在我的博客里搜索 对每个点取出来后把答案放进一个堆里,显然当前的堆顶一定会对答案产生贡献 然后我们考虑每次取出的右端点,它依旧可能产生贡献.即上一次取的最优的$l$把