QOJ.ac

QOJ

IDProblemSubmitterResultTimeMemoryLanguageFile sizeSubmit timeJudge time
#54955#2475. Bank RobberysebinkimWA 18ms3684kbC++7.0kb2022-10-11 18:55:442022-10-11 18:55:47

Judging History

你现在查看的是最新测评结果

  • [2023-08-10 23:21:45]
  • System Update: QOJ starts to keep a history of the judgings of all the submissions.
  • [2022-10-11 18:55:47]
  • 评测
  • 测评结果:WA
  • 用时:18ms
  • 内存:3684kb
  • [2022-10-11 18:55:44]
  • 提交

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;
    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: 18ms
memory: 3684kb

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
Wrong Answer
time: 0ms
memory: 3668kb

input:

6 2
0 1
0 2
0 3
3 4
3 5
0 3
1 0 1

output:

ATTACK
1

result:

wrong answer the contestant was not able to beat me in 1 turns; giving WA