QOJ.ac
QOJ
ID | Problem | Submitter | Result | Time | Memory | Language | File size | Submit time | Judge time |
---|---|---|---|---|---|---|---|---|---|
#54958 | #2475. Bank Robbery | sebinkim | RE | 12ms | 3648kb | C++ | 7.0kb | 2022-10-11 18:58:25 | 2022-10-11 18:58:28 |
Judging History
answer
#include <bits/stdc++.h>
#include <unistd.h>
using namespace std;
using ll=long long;
using ld=double;
using vll=vector<ll>;
using vvll=vector<vector<ll>>;
#define FOR(i,a,b) for(ll i=a;i<(ll)b;++i)
#define F(n) FOR(i,0,n)
#define FF(n) FOR(j,0,n)
#define aa first
#define bb second
#define PB push_back
#define EQ(a,b) (fabs(a-b)<=(fabs(a+b)*EPS))
#define MOD ((ll)(1e9+7))
#define dbg cerr
#define out(n) cout << n << endl
#define INF (1ll << 40)
//#define dbg if(0)cerr
vvll g;
ll n;
bool shouldDefend;
ll minGuards() {
ll res=1;
F(g.size()) {
res += (g[i].size() > 1);
}
return res;
}
vll initialPlacement() {
vll res;
F(g.size()) if (g[i].size() == 1) {
res.PB(i);
break;
}
F(g.size()) if (g[i].size() > 1) res.PB(i);
return res;
}
void print(const vll & a){
for (auto i: a) cout << i << ' ';
cout << endl;
}
ll readAttack() {
ll t;
cin>>t;
if (t == -1) {
exit(0);
}
return t;
}
bool has(vll & a, ll n) {
for (auto i: a) if (i == n) return 1;
return 0;
}
bool dfs(ll v, ll prev, ll goal, const set<ll> & allowed, vector<pair<ll,ll>> & res) {
//dbg << "dfs: " << v << endl;
if (v == goal) {
//out(prev << ' ' << v);
res.PB({prev,v});
return true;
}
for (auto i: g[v]){
if (i == prev || (!allowed.count(i) && i != goal)) continue;
if (dfs(i, v, goal, allowed, res)) {
if (prev != -1) res.PB({prev,v});
return true;
}
}
return false;
}
void marchGuards(int from, int to, vll & pl) {
//dbg << "march from " << from << " to " << to << endl;
set<ll> pls(pl.begin(), pl.end());
vector<pair<ll,ll>> res;
if (!dfs(from, -1, to, pls, res)) {
//dbg << "found no way to defend, losing :(" << endl;
dbg << "ATTACKER WINS" << endl;
assert(!shouldDefend);
exit(0);
}
out(res.size());
for (auto j: res) cout << j.aa << ' ' << j.bb << endl;
pls.erase(from);
pls.insert(to);
pl.clear();
pl.insert(pl.end(), pls.begin(), pls.end());
}
void defendMyself(vll & pl, ll at) {
// find the occupied leaf
dbg << "defending" << endl;
// if occupied, do not move
if (find(pl.begin(), pl.end(), at) != pl.end()) {
out(0);
return;
}
// optimal move is to move with a leaf. If it is not possible, pick a vertex on random
for (auto i: pl) {
if (g[i].size() == 1) {
marchGuards(i, at, pl);
return;
}
}
ll picked = pl[0];
F(pl.size()) if (rand()%(i+1) == 0) picked = pl[i];
marchGuards(picked, at, pl);
}
// compute the difference between the required and actual number of guards in each subtree, if each subtree had no edge to the parent
ll dfs2(ll v, ll prev, vll & mem, const set<ll> & pls) {
ll res = pls.count(v) - (g[v].size() > 1);
for (auto i: g[v]) {
if (i == prev) continue;
res += dfs2(i, v, mem, pls);
}
mem[v] = res - 1;
return res;
}
// compute depth for each node
void dfs3(ll v, ll prev, vll & mem, ll dep) {
mem[v] = dep;
for (auto i: g[v]) {
if (i == prev) continue;
dfs3(i, v, mem, dep+1);
}
}
// find some leaf of a given subtree
ll dfs4(ll v, vll & depth) {
if (g[v].size() == 1) return v;
for (auto i: g[v]) {
if (depth[i] > depth[v]) return dfs4(i, depth);
}
assert(false);
}
ll attackOnEnemy(ll root, const vll & pl) {
vll surplus(n), depth(n);
set<ll> pls(pl.begin(), pl.end());
dfs2(root, -1, surplus, pls);
dfs3(root, -1, depth, 0);
// find undefended leaf and parent
F(n) {
if (g[i].size() == 1 && !pls.count(i) && !pls.count(g[i][0])) {
dbg << "won!!!!!! Muhahahahaha >:O" << endl;
return i;
}
}
// check if all red vertices are occupied, if so attack on a leaf
bool anyRedUnoccupied=false;
F(n) {
if (g[i].size() > 1 && !pls.count(i)) anyRedUnoccupied = true;
}
if (!anyRedUnoccupied) {
F(n) if (g[i].size() == 1) { return i; }
}
// there is some deficient subtree with no guard, find the one deficient subtree and attack its leaf
pair<ll,ll> best = {-INF, -INF};
F(n){
if (!pls.count(i) && surplus[i] < 0 && g[i].size() > 1) {
FF(g[i].size()) {
if (depth[g[i][j]] > depth[i] && surplus[g[i][j]] < 0) {
return dfs4(g[i][j], depth);
}
}
}
}
assert(false);
}
// returns either one or two vertices
vector<ll> treeCenters(const vector<vector<ll>> & g) {
ll n = g.size();
vector<ll> depth(n, -1);
queue<ll> q;
F(n) if (g[i].size() == 1) {
q.push(i);
depth[i] = 0;
}
while (q.size()) {
ll cur = q.front();
q.pop();
F(g[cur].size()) {
if (depth[g[cur][i]] == -1) {
q.push(g[cur][i]);
depth[g[cur][i]] = depth[cur] + 1;
}
}
}
ll mx = -1;
F(n) mx = max(depth[i], mx);
vector<ll> res;
F(n) if (depth[i] == mx) res.PB(i);
return res;
}
char tt;
void test(int guards) {
vll pl = initialPlacement(); // optimal number of guards
//pl.pop_back(); // make it one less than optimal
//pl.erase(find(pl.begin(), pl.end(), 3));
while (pl.size() > guards) {
swap(pl[pl.size()-1], pl[rand()%pl.size()]);
pl.pop_back();
}
//dbg << "initial placement: " << endl;
print(pl);
ll root = treeCenters(g)[0];
//dbg << "rooted at " << root << endl;
F(1000){
ll at = attackOnEnemy(root, pl);
dbg << "attacking on " << at << "!" << endl;
#ifdef WAITFORKEY
cin>>tt;
#endif
if (has(pl,at)) out("0");
else {
defendMyself(pl, at);
}
#ifdef WAITFORKEY
cin>>tt;
#endif
}
assert(shouldDefend);
dbg << "DEFENDER WINS" << endl;
exit(0);
}
vll readPlacement(ll gd) {
vll pl(gd);
F(gd)cin>>pl[i];
dbg << "current placement: " << endl;
for (auto i: pl) dbg << i << ' ';
dbg << endl;
return pl;
}
void updateByMoves(vll & pl) {
ll k;
cin>>k;
multiset<ll> pls(pl.begin(),pl.end());
F(k){
ll u,v;
cin>>u>>v;
pls.erase(pls.find(u));
pls.insert(v);
}
pl.clear();
pl.insert(pl.begin(), pls.begin(), pls.end());
}
int main(){
ios::sync_with_stdio(0);cin.tie(0);
srand(time(0));
//dbg << "Let's play a game >:)" << endl;
ll gd;
cin>>n>>gd;
g=vvll(n);
F(n-1){
ll u,v;
cin>>u>>v;
g[u].PB(v);
g[v].PB(u);
}
bool defend=0;
//dbg << "min guards: " << minGuards() << endl;
/*gd = minGuards()-1;*/
if (gd >= minGuards()){
out("DEFEND");
shouldDefend=1;
defend=1;
} else {
shouldDefend=0;
out("ATTACK");
}
//test(gd);
if (defend) {
dbg << "defending" << endl;
vll pl = initialPlacement();
print(pl);
F(1000){
ll at = readAttack();
defendMyself(pl, at);
}
} else { // attack
dbg << "attacking" << endl;
ll root = treeCenters(g)[0];
dbg << "rooted at " << root << endl;
vll pl = readPlacement(gd);
dbg << "got the placement, planning my next attack >:)" << endl;
for (; ; ) {
out(attackOnEnemy(root, pl));
updateByMoves(pl);
}
}
return 0;
}
Details
Tip: Click on the bar to expand more detailed information
Test #1:
score: 100
Accepted
time: 12ms
memory: 3648kb
input:
6 3 0 1 0 2 0 3 3 4 3 5 4 2 5 1 2 5 1 5 1 2 1 2 1 5 4 2 5 1 2 5 4 5 2 1 4 5 2 5 1 2 4 2 4 5 1 2 5 4 5 4 1 5 1 2 5 2 5 1 2 4 5 4 2 1 5 1 2 1 4 5 4 2 5 4 1 2 4 2 4 1 2 5 1 2 4 2 1 5 4 5 2 1 2 4 1 4 5 4 5 2 4 5 4 2 5 2 1 5 4 5 2 4 5 1 5 2 5 2 5 1 4 2 1 4 1 5 4 5 2 4 5 1 5 2 1 2 1 4 1 4 2 1 5 1 4 1 2 5 ...
output:
DEFEND 1 0 3 3 3 4 0 3 1 0 3 0 2 3 0 4 3 3 3 5 0 3 2 0 3 0 1 3 0 5 3 2 0 2 1 0 3 3 5 0 3 2 0 3 0 1 3 0 5 3 3 3 5 0 3 1 0 3 0 1 3 0 5 3 2 0 2 1 0 2 0 1 2 0 2 0 2 1 0 2 0 1 2 0 3 3 5 0 3 1 0 2 3 4 5 3 3 0 2 3 0 4 3 3 3 5 0 3 2 0 3 0 1 3 0 5 3 2 0 2 1 0 3 3 5 0 3 2 0 2 3 4 5 3 2 3 5 4 3 3 0 2 3 0 5 3 ...
result:
ok
Test #2:
score: -100
Dangerous Syscalls
input:
6 2 0 1 0 2 0 3 3 4 3 5 0 3 1 0 1
output:
ATTACK 1 2