LOJ #3161. 「NOI2019」I 君的探险
题目叙述
交互题,\(n\) 个点 \(m\) 条边的图,每次可以做四种操作之一:
- 给一个点以及他周围的点取反
- 查询当前节点的状态
- 汇报一条边已经被找出
- 检查一个点连出去的所有边是否都被找出来了
题解
比较有技术含量的是树的部分分以及正解。
树
- 考虑二进制分组之后能做到什么。发现可以做到的是求出一个点周围所有结点的异或和。
- 然后每次至少能够剥叶子,判断每个节点异或和和这个节点是否右边(这是可以判断的!!),然后就找出来整棵树了。
图
- 考虑部分分每个点之和前面一个节点连边的做法。直接整体二分即可。
- 这个做法在正常的图上如果想要判定,判定的规则如果还是,这个点还是亮的,那么判定出来的是什么。
- 可以发现,一定能够判定出来的节点是满足向前连接的节点数量是奇数的所有节点。
- 多随机几个排列,每个点都有不少概率满足和前面的奇数个节点连边。
总结
- 要思考一些部分分解法放到整个题目里面有什么效果。不管是树的暴力还是图的做法。
代码
注意前面一些测试点还是得写暴力。
#include "explore.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <algorithm>
#include <vector>
#define FOR(i,l,r) for(int i=(l),i##ADJK=(r);i<=i##ADJK;++i)
#define ROF(i,r,l) for(int i=(r),i##ADJK=(l);i>=i##ADJK;--i)
using namespace std;
const int MN=2e5+5;
namespace force{
int last[MN], N, M, diff[MN];
bool vis[MN];
void subtask1() {
for (int i = 0; i < N-1; ++i) {
modify(i);
for (int j = i + 1; j < N; ++j) {
int tmp = query(j);
if (tmp != last[j]) {
last[j] = tmp;
report(i, j);
}
}
}
}
void subtask2() {
for (int i = 0; (1 << i) < N; ++i) {
for (int j = 0; j < N; ++j)
if ((j >> i) & 1) modify(j);
for (int j = 0; j < N; ++j) {
int tmp = query(j);
if (tmp != last[j]) {
diff[j] |= (1 << i);
last[j] = tmp;
}
}
}
for (int i = 0; i < N; ++i)
if (!vis[i]) {
report(i, i ^ diff[i]);
vis[i] = 1;
vis[i ^ diff[i]] = 1;
}
}
void main(int N1, int M1) {
N = N1;
M = M1;
if (N <= 500) subtask1();
else if (M == N / 2)
subtask2();
}
}
namespace s4{
int per[MN];
bool sta[MN];
vector<int> tr[MN*4],G[MN];
void solve(int o,int l,int r){
if(l==r){
for(int &x:tr[o]){
G[per[l]].push_back(x),G[x].push_back(per[l]);
report(x,per[l]);
}
return;
}
int mid=(l+r)>>1;
FOR(i,l,mid){
modify(per[i]);
for(int &x:G[per[i]])sta[x]^=1;
}
FOR(i,mid+1,r)if(query(per[i])!=sta[per[i]])tr[o<<1].push_back(per[i]);
for(int &x:tr[o]){
if(query(x)!=sta[x])tr[o<<1].push_back(x);
else tr[o<<1|1].push_back(x);
}
FOR(i,l,mid){
modify(per[i]);
for(int &x:G[per[i]])sta[x]^=1;
}
solve(o<<1,l,mid),solve(o<<1|1,mid+1,r);
}
void main(int N,int M){
int n=N;
FOR(i,1,N)per[i]=i-1;
if(N%10==7)return solve(1,1,n);
while(n){
random_shuffle(per+1,per+n+1);
FOR(o,1,n*4)tr[o].clear();
solve(1,1,n);
int tmp=0;
FOR(i,1,n)if(!check(per[i]))per[++tmp]=per[i];
n=tmp;
}
}
}
void explore(int N,int M){
if(N<=500||M==N/2)force::main(N,M);
else s4::main(N,M);
}

浙公网安备 33010602011771号