[JOISC2020] カメレオンの恋 题解
前言
没想到乱搞真给做出来了。
询问上限
思路
定义
首先一个直观想法是,每次询问一个点对
思考一下,显然是不对的,因为有单相思的存在。
所以对于
-
-
喜欢 。 -
喜欢 。
看到这种情况,强迫症告诉你你应该去建个边。
考虑一个图,对于一个点对
-
连一条无向边。 -
喜欢 , 向 连一条有向边。
在不考虑询问次数的情况下,我们只需要找到有向边就可以从而确定答案。
我们发现一个点最多只能与三条边相关联。
相当于说如果一个点
-
它的度数为
,只有可能是 的那一条,直接就找到了原色相同的哪一个。 -
它的度数为
,显然是一条 和一条单相思的边。但是显然这条单相思的边 的双方必须满足 不然实际上与度数为 是等价的。在这种情况下,必然还存在一条 或者 的边,并且由于题目条件限制,双方原色不相等。可是这样的话就构成了第三条边,所以矛盾。故不存在度数为 的点。 -
它的度数为
。考虑与它相关联的点分别为 。我们将这四个点三个三个配对进行 ,可以发现,只有喜欢 的点, ,和与 原色相同的点才会 之后为 ,其他的都大于等于 。故我们可以借此找到一条有向边 喜欢 。至于 喜欢的那个点,显然会在那个点被找到,也就找到了所有有向边。
故此我们找到了所有有向边,并借此找到无向边,从而确定答案。
但是这样查询的话是次数是
考虑进行一些优化。
第一时间想到一个分治的技巧,即对于一个点
但是你还是发现一个问题,就是说要是
所以考虑这样循环分治:
-
在当前点集(最初就是
)中找到一个独立集。(找独立集就是枚举当前点集,如果能加入就加入,判断方法就是 是否等于当前独立集大小 ) -
将当前点集减去这个独立集。
-
枚举当前点集中的点,在独立集中进行分治,找到能连的边。
-
如果当前点集为空,退出循环。
虽然的分治层数在
但是显然存在一些常数问题。所以实现一定要精细。
具体见代码:
//洛谷代码与OJ代码不同
#include<bits/stdc++.h>
#include "chameleon.h"
using namespace std;
int ask(vector<int> &p, int x)
{
if(p.size() == 1) return p[0];
vector<int> q;
int l = 0, r = p.size() - 1;
int mid = (l + r) >> 1;
for (int i = l; i <= mid; ++i) q.push_back(p[i]);
q.push_back(x);
if(Query(q) != q.size()) return q.pop_back(), ask(q, x);
q.clear();
for (int i = mid + 1; i <= r; ++i) q.push_back(p[i]);
return ask(q, x);
}
void Solve(int N)
{
int n = N;
vector<int> ans[5005];
set<int> p[10005], q;
int id[10005];
for (int j = 1; j <= 2 * n; ++j) id[j] = j, q.insert(j);
while(q.size())
{
int now = *q.begin(); q.erase(now);
vector<int> pvp;
pvp.push_back(now);
// random_shuffle(id + 1, id + 2 * n + 1);
for (int qwq = 1; qwq <= 2 * n; ++qwq)
{
int i = id[qwq];
if(!q.count(i)) continue;
pvp.push_back(i);
if(Query(pvp) != pvp.size()) pvp.pop_back();
else q.erase(i);
}
for (int i = 1; i <= 2 * n; ++i)
{
if(!q.count(i)) continue;
if(ans[i].size() >= 3) continue;
set<int> del;
while(ans[i].size() < 3)
{
vector<int> qp;
for (int i = 0; i < pvp.size(); ++i) if(!del.count(pvp[i])) qp.push_back(pvp[i]);
qp.push_back(i);
if(Query(qp) == qp.size()) break;
int id = ask(qp, i);
ans[id].push_back(i), ans[i].push_back(id);
p[id].insert(i), p[i].insert(id);
del.insert(id);
}
}
}
for (int i = 1; i <= 2 * n; ++i)
{
if(ans[i].size() == 3)
{
vector<int> r;
r.push_back(i), r.push_back(ans[i][0]), r.push_back(ans[i][1]);
if(Query(r) == 1) p[i].erase(ans[i][2]), p[ans[i][2]].erase(i);
r.clear();
r.push_back(i), r.push_back(ans[i][0]), r.push_back(ans[i][2]);
if(Query(r) == 1) p[i].erase(ans[i][1]), p[ans[i][1]].erase(i);
r.clear();
r.push_back(i), r.push_back(ans[i][2]), r.push_back(ans[i][1]);
if(Query(r) == 1) p[i].erase(ans[i][0]), p[ans[i][0]].erase(i);
r.clear();
}
}
for (int i = 1; i <= 2 * n; ++i)
{
if(*p[i].begin() < i) Answer(*p[i].begin(), i);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】