Topcoder SRM 744 (Div.1 + Div.2)

Posted nishikino-curtis

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Topcoder SRM 744 (Div.1 + Div.2)相关的知识,希望对你有一定的参考价值。

Topcoder SRM 744 (Div.1 + Div.2)

解题口胡报告 + 吐槽.

发现其实SRM的题没有很不可做. 不过感觉赛时肯定还是会爆负, 让我75min想到写完这些题是做不到.

Div.2 250

入门题, 把一个左开右闭区间分成三个左开右闭区间.

没写, 除一除加一加就好了.

Div.2 500

入门题, 问一个区间里有多少数是完全平方数且满足各数位的数字是波动的.

我居然天真地想数位DP

数据范围不大, 反正枚举平方数再检查是够的.

Div.2 1000 / Div.1 250

有一个(N imes M)矩阵, 格子((i,j))的权值是(max{(i,j)} mod{3}). 求一个给定子矩阵的权值和. (N, M le 10^9).

什么嘛矩阵是从0开始存的?

转成二维前缀和. 就讨论一下对角线, 对角线两边, 和较大的那一维多出来的一块, 加起来就行.

ModularQuadrant.cpp:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int summ[3] = {0, 1, 3};
const int val[3] = {3, 1, 2};

inline ll seq(int init, int count) {
  ll tail = (ll)init + 3ll * (count - 1);
  return (init + tail) * count / 2ll;
}

class ModularQuadrant {
public:
  ll sum(int, int, int, int);
  ll prefix(int, int);
};

ll ModularQuadrant::sum(int r1, int c1, int r2, int c2) {
  return ModularQuadrant::prefix(r2, c2) - 
         ModularQuadrant::prefix(r2, c1 - 1) -
         ModularQuadrant::prefix(r1 - 1, c2) +
         ModularQuadrant::prefix(r1 - 1, c1 - 1);
}
ll ModularQuadrant::prefix(int r, int c) {
  if (r == -1 || c == -1) return 0;
  int lim = min(r, c) + 1;
  ll ang = (lim / 3) * 3 + summ[lim % 3];
  ll tri = seq(1, lim / 3) * 1ll + seq(2, lim / 3) * 2ll + seq(0, lim / 3) * 0; 
  if (lim % 3 == 1) {
    tri += (lim - 1) * 0;
  } else if (lim % 3 == 2) {
    tri += 1ll * (lim - 1) + 0ll * (lim - 2);
  }
  tri *= 2ll;
  if (r == c) return tri + ang;
  ll ret = 0; int bgn = lim, eed = max(r, c), len = eed - bgn + 1;
  ret += (len / 3) * 1ll * lim + (len / 3) * 2ll * lim + (len / 3) * 0ll * lim;
  if (len % 3 == 1) {
    ret += 1ll * val[bgn % 3] * lim;
  } else if (len % 3 == 2) {
    ret += 1ll * val[bgn % 3] * lim + 1ll * val[(bgn + 1) % 3] * lim;
  }
  return ret + ang + tri;
}

Div.1 500

给一个01矩阵, 每次可以操作一个((i,j)), 将(i)行和(j)列除了她自己以外的格子都取反.

问有没有可能把全部格子都变成1. (N, M le 2000).

这就是比较经典的套路.

首先观察一下操作, 发现相当于把行取反再把列取反. 这和我们见过的题相比多出的限制就是行列操作次数必须相等, 只要最后查一下就行了.

枚举第一行反或不反, 把后面的行都和她对齐, 然后看第一行还有哪些是0, 就要对列取反. 把次数统计起来.

UniformingMatrix.cpp:

#include <bits/stdc++.h>

using namespace std;

const string YES = "Possible";
const string NO = "Impossible";

int line[200];

class UniformingMatrix {
public:
  string isPossible(vector<string> M);
};

string UniformingMatrix::isPossible(vector<string> M) {
  int n = M.size(), m = M[0].size();
  bool sol = true; int row = 0, col = 0;
  for (int i = 0; i < m; ++i)
    line[i] = M[0][i] - '0';
  for (int i = 1; i < n; ++i) {
    int lst = line[0] ^ (M[i][0] - '0');
    for (int j = 1; j < m; ++j) {
      if ((M[i][j] - '0') ^ line[j] != lst) {
        sol = false;
        break;
      }
    }
    if (sol == false) break;
    if (lst) row++;
  }
  if (sol) {
    for (int i = 0; i < m; ++i)
      if (!line[i]) col++;
    if (row == col) return YES;
  }
  for (int i = 0; i < m; ++i)
    line[i] = (1 ^ (M[0][i] - '0'));
  row = 1; col = 0;
  for (int i = 1; i < n; ++i) {
    int lst = line[0] ^ (M[i][0] - '0');
    for (int j = 1; j < m; ++j) {
      if ((M[i][j] - '0') ^ line[j] != lst) {
        sol = false;
        break;
      }
    }
    if (sol == false) break;
    if (lst) row++;
  }
  if (sol) {
    for (int i = 0; i < m; ++i)
      if (!line[i]) col++;
    if (row == col) return YES;
  }
  return NO;
}

Div.1 1000

给一颗有根树. 每个点有需求和花费. 给每个点赋值使得每个点到根的路径上的权值和不小于需求, 代价是值乘以点权.

最小化花费. (N le 10^5).

TC题正解总是用匪夷所思的简单做法我居然想要DP

自下而上地贪心考虑. 我们的目的首先是满足权值下限, 先不考虑祖先(后面会更新). 考虑子树中所有和当前节点路径上权值和为0(不包括端点)的节点. 做到这里时他们的要求肯定是满足的. 那么我们可以考虑, 如果减小他们的权值, 而在当前节点增加同样的权值, 如果子节点的费用和大于当前节点的费用, 这一步调整就是优的.

如何快速做这个过程呢? 可以用小根堆来维护各点权值. 初始先将儿子压入堆. 然后做操作. 当堆顶被减为0时, 把堆顶节点的堆和当前堆合并. 为了保证复杂度要启发式合并. (感觉左偏树也是可以的).

然后还有一些细节, 大概就是每次减小量的上界还有满足条件就退出之类的, 代码里都写了.

还有这鬼畜的加密读入

CoverTreePaths.cpp:

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int maxn = 1e+5 + 5;

struct edge {
  int to, nxt;
}e[maxn];
struct node {
  ll val; int id;
  node(ll v_ = 0, int i_ = 0) : val(v_), id(i_) {}
  inline bool operator<(const node &rhs) const {
    return (val == rhs.val) ? (id > rhs.id) : (val > rhs.val);
  }
};
int f[maxn], cs[maxn], nd[maxn];
ll sumcs[maxn], sub[maxn];
int ptr, lnk[maxn];
int v[maxn];
priority_queue<node> q[maxn];

inline void add(int bgn, int end) {
  e[++ptr] = (edge){end, lnk[bgn]};
  lnk[bgn] = ptr;
}
void merge(int frm, int to) {
  while (q[frm].size()) {
    node u = q[frm].top(); q[frm].pop();
    u.val -= sub[frm];
    u.val += sub[to];
    q[to].push(u);
  }
}

class CoverTreePaths {
public:
  ll minimumCost(int, 
                 vector<int>, vector<int>, vector<int>, vector<int>);
};

ll CoverTreePaths::minimumCost(int n, 
               vector<int> p, vector<int> d, 
               vector<int> c, vector<int> params) {
  // PreProcess
  for (vector<int>::size_type i = 0; i < p.size(); ++i) {
    f[i + 1] = p[i];
    add(f[i + 1], i + 1);
  }
  int bgn = p.size();
  for (int i = bgn; i < n - 1; ++i) {
    p.push_back((1ll * params[0] * p[i - 1] + params[1]) % (i + 1));
    f[i + 1] = p[i];
    add(f[i + 1], i + 1);
  }
  for (vector<int>::size_type i = 0; i < d.size(); ++i) {
    nd[i] = d[i];
  }
  bgn = d.size();
  for (int i = bgn; i < n; ++i) {
    d.push_back((1ll * params[2] * d[i - 1] + params[3]) % params[4] + 1);
    nd[i] = d[i];
  }
  for (vector<int>::size_type i = 0; i < c.size(); ++i) {
    cs[i] = c[i];
  }
  bgn = c.size();
  for (int i = bgn; i < n; ++i) {
    c.push_back((1ll * params[5] * c[i - 1] + params[6]) % params[7] + 1);
    cs[i] = c[i];
  }

  // SolvingProblem
  ll ans = 0;
  for (int k = n - 1; ~k; --k) {
    sumcs[k] = 0;
    sub[k] = 0;
    for (int p = lnk[k]; p; p = e[p].nxt) {
      int y = e[p].to;
      sumcs[k] += cs[y];
      q[k].push(node(v[y], y));
    }
    while (1) {
      ll delta = 0;
      if (!q[k].size()) {
        if (v[k] >= nd[k]) {
          break;
        } else delta = nd[k] - v[k];
      } else if (cs[k] < sumcs[k]) {
        delta = q[k].top().val - sub[k];
      } else if (v[k] < nd[k]) {
        delta = min((ll)nd[k] - v[k], q[k].top().val - sub[k]);
      } else break;

      ans += delta * cs[k];
      ans -= delta * sumcs[k];
      sub[k] += delta;
      v[k] += delta;

      vector<node> waitList;
      while (q[k].size() && q[k].top().val == sub[k]) {
        node u = q[k].top(); q[k].pop();
        waitList.push_back(u);
        sumcs[k] -= cs[u.id];
      }

      for (auto u : waitList) {
        sumcs[k] += sumcs[u.id];
        if (q[u.id].size() <= q[k].size()) {
          merge(u.id, k);
        } else {
          merge(k, u.id);
          q[k] = q[u.id];
          sub[k] = sub[u.id];
        }
      }
    }
  }
  return ans;
}

没啦.

以上是关于Topcoder SRM 744 (Div.1 + Div.2)的主要内容,如果未能解决你的问题,请参考以下文章

TopCoder[SRM513 div 1]:(500)

TopCoder SRM 596 DIV 1 250

TopCoder SRM 561 Div 1 - Problem 1000 Orienteering

TopCoder SRM 558 Div 1 - Problem 1000 SurroundingGame

TopCoder SRM 559 Div 1 - Problem 900 CircusTents

TopCoder SRM 560 Div 1 - Problem 1000 BoundedOptimization & Codeforces 839 E