首页 > 其他分享 >CF1969F Card Pairing

CF1969F Card Pairing

时间:2024-10-18 23:33:57浏览次数:7  
标签:相同 int sum Pairing bit define rep Card CF1969F

少有的独自做出来的 *3000,还是很有成就感的!

集中注意力读题,首先注意到每一时刻牌数为 \(k\),而牌的种类也为 \(k\),如果实际牌的种类数小于 \(k\),那么是很简单的情况,现在考虑实际牌的种类数等于 \(k\) 的情况。

观察过程,首先发现如果有相同的牌直接丢就行,过程中还会出现没有牌相同的情况,显然这种情况每个牌的数量为 \(1\),启发我们使用 dp,设 \(f_i\) 表示已经考虑完 \(i\) 张牌且当前没有相同牌,能获得的最多硬币数。

首先呼之欲出的是 \(\mathcal{O}(n^2k^2)\) 的愚蠢做法。就是枚举我们删哪两张牌,再模拟出后面第一次出现没有相同牌状态的位置,并完成转移。

这样做是低效的。我们发现我们可以直接转移,在转移中变成判定问题,\(f_j\) 能转移到 \(f_i\),当且仅当区间 \([i+1,j]\) 从当前状态继续操作完成后出现无相同牌状态,且中途一直有相同牌。注意到删相同牌不会改变奇偶性,而加入会,我们发现这个判定过程实际上是一个异或加入牌的过程,而一个区间能满足条件当且仅当存在两种牌出现个数为奇数,直接异或哈希即可。

还有一种很烦人的情况,就是永远都不会出现无相同牌的状态了,这时的答案与实际牌的种类数小于 \(k\) 的类似,他的答案就是 \(\sum_{i=1}^k \lfloor \frac{s_i}{2}\rfloor\)。\(s_i\) 表示第 \(i\) 种手牌的数量。然而,对于当前 \(f_i\) 的答案来说,\(s_i\) 是对于 \([i,n]\) 这个后缀算出来的,并且每个都会加一,因为当前每种牌各有一张。而这时我们要删除两张牌,显然我们因尽可能删除 \(s_i\) 为奇数的牌,因为这样不会减少答案。枚举牌对显然不优,但我们可以将判断转为简单计数。

然后还有个细节就是起点问题,直接模拟找起点就行了。

总时间复杂度 \(\mathcal{O}(n^2)\)。

代码:

#include <bits/stdc++.h>
#define rep(i, l, r) for (int i = l; i <= r; ++ i)
#define rrp(i, l, r) for (int i = r; i >= l; -- i)
#define pii pair <int, int>
#define eb emplace_back
#define id(x, y) m * ((x) - 1) + (y)
#define ls p << 1
#define rs ls | 1
using namespace std;
constexpr int N = 1e3 + 5, P = 1e9;
constexpr double PI = acos (-1.0);
typedef unsigned long long ull;
inline int rd () {
  int x = 0, f = 1;
  char ch = getchar ();
  while (! isdigit (ch)) {
    if (ch == '-') f = -1;
    ch = getchar ();
  }
  while (isdigit (ch)) {
    x = (x << 1) + (x << 3) + ch - 48;
    ch = getchar ();
  }
  return x * f;
}
int qpow (int x, int y) {
  int ret = 1;
  for (; y; y >>= 1, x = x * x % P) if (y & 1) ret = ret * x % P;
  return ret;
}
int n, k;
int a[N], b[N], f[N];
bitset <N> bit;
ull w[N];
unordered_map <ull, pii> mp;
unordered_map <ull, bool> mmp;
int s1[N][N], s[N];
int main () {
  // freopen ("1.in", "r", stdin);
  // freopen ("1.out", "w", stdout);
  n = rd (), k = rd ();
  rep (i, 1, n) a[i] = b[i] = rd ();
  sort (b + 1, b + n + 1);
  int kk = unique (b + 1, b + n + 1) - b - 1;
  if (kk < k) {
    rep (i, 1, n) ++ s[a[i]];
    int sum = 0;
    rep (i, 1, k) sum += s[i] / 2;
    cout << sum;
    return 0;
  }
  rep (i, 1, n) a[i] = lower_bound (b + 1, b + k + 1, a[i]) - b;
  mt19937_64 rnd (time (0));
  rep (i, 1, k) {
    w[i] = rnd ();
    rep (j, 1, i - 1) mp[w[i] ^ w[j]] = pii (i, j);
  }
  rrp (i, 1, n) {
    ull now = 0;
    rep (j, i, n) ++ s1[i][a[j]];
    int s2[3] = {0, 0, 0}, s3[2] = {0, 0}, sum = 0;
    rep (j, i, n) {
      now ^= w[a[j]];
      if (mp.find (now) != mp.end () && ! mmp[now]) {
        pii p = mp[now];
        mmp[now] = 1;
        f[i - 1] = max (f[i - 1], f[j] + (j - i + 1) / 2 - 1);
        int x = s1[i][p.first], y = s1[i][p.second];
        if ((x & 1) ^ (y & 1)) ++ s2[1];
        else if (x & 1) ++ s2[0]; else ++ s2[2];
      }
    }
    now = 0;
    rep (j, i, n) {
      now ^= w[a[j]];
      mmp[now] = 0;
    }
    rep (j, 1, k) sum += (s1[i][j] + 1) / 2, ++ s3[s1[i][j] & 1];
    if (s3[1] * (s3[1] - 1) / 2 > s2[0]) f[i - 1] = max (f[i - 1], sum - 2);
    if (s3[0] * s3[1] > s2[1]) f[i - 1] = max (f[i - 1], sum - 1);
    if (s3[0] * (s3[0] - 1) / 2 > s2[2]) f[i - 1] = max (f[i - 1], sum);
  }
  int ans = -1;
  rep (i, 1, n) {
    if (bit[a[i]] == 1) bit[a[i]] = 0;
    else bit[a[i]] = 1;
    if (bit.count () == k) {
      ans = max (ans, (i - k) / 2 + f[i]);
      break;
    }
  }
  if (ans == -1) {
    rep (i, 1, n) ++ s[a[i]];
    int sum = 0;
    rep (i, 1, k) sum += s[i] / 2;
    cout << sum; return 0;
  }
  cout << ans;
}

标签:相同,int,sum,Pairing,bit,define,rep,Card,CF1969F
From: https://www.cnblogs.com/lalaouyehome/p/18475224

相关文章

  • 集合论(ZFC)之基数(Cardinality)浅析
    直观感受(Intuition)与核心思想(CoreIdea)        集合的基数(Cardinality)是衡量集合的大小,也就是集合中元素的个数。但是,由于无限集与超限集的存在,因此,单纯用自然数去描述集合的大小是不可行的。自然数只能描述有限集的大小。所以,需要一个新的概念去描述集合的大小,那就是......
  • E. Card Game
    E.CardGameInthemostpopularcardgameinBerland,adeckof$n\timesm$cardsisused.Eachcardhastwoparameters:suitandrank.Suitsinthegamearenumberedfrom$1$to$n$,andranksarenumberedfrom$1$to$m$.Thereisexactlyonecardin......
  • 当 smartcardsimulator.dll 加载错误时的解决策略
    smartcardsimulator.dll文件通常与智能卡模拟器或智能卡相关的应用程序有关。智能卡模拟器是一种软件工具,用于模拟智能卡的行为,以便开发人员测试和调试智能卡应用。smartcardsimulator.dll文件负责处理智能卡模拟的相关功能。当您看到“smartcardsimulator.dll加载错误”......
  • 2024牛客多校第二场 - I. Red Playing Cards
    思路与官方题解一样,不过我采用了递归的写法,这样就可以避免排序等操作。另外还要注意递归的时候不能让多个不同的递归函数同时修改一个数组,否则这个数组同时被多个函数使用,会很混乱。我这里把它开成了二维来避免这个问题。代码如下:#include<cstdio>#include<algorithm>usingn......
  • ABC225F String Cards
    题意给你\(n\)个串\(s_i\),你需要选出\(k\)个串并按照某个顺序拼接起来形成的字符串字典序最小。\(n,k,|s|\le50\)。分析由于顺序不固定,所以我们无法直接DP。而状压的复杂度也太高了,怎么办呢?考虑钦定一个顺序,使得按照这个顺序排列字符串一定最优。一个经典的错误想法......
  • CF2019C Cards Partition
    涉及知识点:鸽巢原理,贪心前言唐诗题,赛时都已经想到了所有性质,以为要从数学方法上求解,却没想到就是个纯贪心题……题意Link给你一堆数,\(1,2,3,\dots,n\),分别有\(a[1],a[2],a[3],\dots,a[n]\)个,你还可以添加不超过\(k\)个数(当然这些数得是\(1\simn\)中的整数),你需要将它们......
  • DiscardOldestPolicy
    特点:当任务无法被线程池执行时,线程池会丢弃队列中最旧的未处理任务,然后尝试重新提交当前任务。使用场景:适用于对新任务优先级较高的场景,当线程池无法接受新任务时,会丢弃一些等待时间较长的旧任务,以便接受新任务。示例代码:importjava.util.concurrent.*;publicclassDiscardOldest......
  • 如何订阅支付DeepL,订阅DeepL Pro以及申请DeepL API?深度解析DeepL,虚拟信用卡WildCard绑
    十里不同音,五里不同调在现今世界中,跨语言的交流能力愈发重要,无论是国际友人之间的沟通交流,还是与客户或者合作伙伴之间的业务沟通,高质量的语言翻译都是一种刚性需求。今天,我们就来看一家这样的独角兽企业——一个机器翻译平台DeepL,它可以立即准确、轻松地将书面内容翻译......
  • GZOI2024 Day1 T2 card
    GZOI2024Day1T2card首先最后一张牌可能不会弃满\(b_i\)张牌。而如果我们要打出若干张牌,肯定想要最后打出\(b_i\)最大的那张牌,这样显然更划算。因此要先按照\(b_i\)排序。首先很容易想到背包。把同类牌拆成\(c_i\)个,然后直接背包:\(f_{i,j}\)表示遍历到第\(i\)张牌,......
  • 【思考模型框架】BSC,Balance Scorecard(平衡计分卡),帮助企业全面、系统地制定和实施战略
    一、定义BSC,全称为BalancedScorecard(平衡计分卡)BSC,是一种战略规划和管理工具。BSC,是一种战略管理和绩效评估工具。BSC,不仅仅是一个评估工具,更是一种战略执行框架。BSC,从财务、客户、内部运营、学习与成长四个维度出发BSC,通过提供一个全面的框架来评估组织绩效,涵盖了......