Submission #298856

#TimeUsernameProblemLanguageResultExecution timeMemory
298856user202729Werewolf (IOI18_werewolf)C++17
100 / 100
1367 ms124584 KiB
// moreflags=grader.cpp

#include "werewolf.h"
#include<vector>
#include<set>
#include<climits>
#include<algorithm>
#if not LOCAL
#define NDEBUG
#endif
#include<cassert>

struct Dsu{ // with path compression but without union by rank
	std::vector<int> data; // positive: parent, negative: ~(minimum in component)
	Dsu(int number): data(number){
		for(int node=0; node<number; ++node)
			data[node]=~ node;
	}
	int root(int node){return data[node]>=0 ? data[node]=root(data[node]): node;}
	int minimumInComponent(int node){
		return ~data[root(node)];
	}
	bool join(int first, int sec){
		first=root(first); sec=root(sec);
		if(first==sec) return false;
		data[first]=std::max(data[first], data[sec]); // inverted
		data[sec]=first;
		return true;
	}
};

std::vector<int> check_validity(int N, std::vector<int> X, std::vector<int> Y,
                                std::vector<int> S, std::vector<int> E,
                                std::vector<int> L, std::vector<int> R) {
	std::vector<std::vector<int>> greaterAdd(N), lessAdd(N);
	for(int index=0; index<(int)X.size(); ++index){
		auto const [a, b]=std::minmax({X[index], Y[index]});
		assert(a!=b);
		greaterAdd[a].push_back(b);
		lessAdd[N-1-b].push_back(N-1-a);
	}

	auto const process=[&](std::vector<std::vector<int>>& add)->std::vector<int>{
		// add: adjacency list (elements of list [i] must be strictly greater than i)
		// also reuse add for the children of the resulting par
		// (node n is for -1)
		std::vector<int> par(add.size(), -1);
		Dsu dsu((int)add.size());
		for(auto index=(int)add.size(); index--;){
			for(auto other: add[index]){
				other=dsu.minimumInComponent(other);
				if(other>index){
					auto const success=dsu.join(index, other);
					assert(success);
					assert(par[other]==-1);
					par[other]=index;
				}else assert(other==index);
			}
		}

		for(auto& it: add) it.clear();
		add.emplace_back();
		for(int node=0; node<(int)par.size(); ++node){
			(par[node]<0 ? add.back(): add[par[node]]).push_back(node);
		}

		return par;
	};

	std::vector<int> greaterPar=process(greaterAdd);
	std::vector<int> lessPar=process(lessAdd);
	// greaterPar: the equivalent structure of traversing with the additional condition (vertex >= L)
	// for some L
	// ( i -> greaterPar[i] ) where greaterPar[i]<i

	// lessPar: vice versa, but with flipped vertex indices

	struct Jump{
		std::vector<std::vector<int>> data;
		// assumes -1 is the virtual root
		Jump(std::vector<int> value): data{std::move(value)}{
			for(int step=1; step<(int)data.back().size(); step<<=1){
				std::vector<int> const& a=data.back();
				auto b=a;
				bool useful=false;
				for(auto& it: b) if(it>=0){
					it=a[it];
					if(it>=0) useful=true;
				}
				if(useful)
					data.push_back(std::move(b));
				else break;
			}
		}
		int get(int node, int least)const{
			// assumes par[node]<node for all node, find minimum ancestor >=least
			for(auto layer=data.size(); layer--;)
				if(data[layer][node]>=least) node=data[layer][node];
			return node;
		}
	};
	Jump greaterJump(std::move(greaterPar)), lessJump(std::move(lessPar));

	struct Query{int node, index;};
	std::vector<std::vector<Query>> queries(N);
	// [node1] = (node2, query): query with index==query -> greater subtree rooted at node1
	// has any common vertex with less subtree rooted at node2?

	for(int query=0; query<(int)S.size(); ++query){
		int node1=greaterJump.get(S[query], L[query]);
		int node2=lessJump.get(N-1-E[query], N-1-R[query]);
		queries[node1].push_back({node2, query});
	}

	std::vector<int> result(S.size());

	// solve all queries

	/* // * not necessary
	auto const subtreeSize=[&]{ // of greater
		std::vector<int> result(N);
		auto const work=[&](auto work, int node)->int{
			auto cur=1;
			for(auto other: greaterAdd[node])
				cur+=work(work, other);
			return result[node]=cur;
		};
		for(auto it: greaterAdd[N]) work(work, it);
		for(auto it: result) assert(it>0);
		return result;
	}();
	*/

	auto const merge=[&](std::set<int> first, std::set<int> sec){
		if(first.size()<sec.size()) std::swap(first, sec);
		for(auto it: sec){
			auto const success=first.insert(it).second;
			assert(success);
		}
		return first;
	};

	std::vector<int> lessFirst(N), lessLast(N); // first and last index in preorder traversal of the less tree
	{ // construct ^
		int cur=0;
		auto const work=[&](auto work, int node)->void{
			assert(lessFirst[node]==0);
			lessFirst[node]=cur++;
			for(auto other: lessAdd[node])
				work(work, other);
			lessLast[node]=cur;
		};
		for(auto node: lessAdd[N]){
			work(work, node);
		}
		assert(cur==N);
	}

	// wait it's not necessary to compute subtreeSize if std::set is used anyway
	auto const work=[&](auto work, int node)->std::set<int>{
		std::set<int> curSet{lessFirst[N-1-node]};
		for(auto other: greaterAdd[node])
			curSet=merge(std::move(curSet), work(work, other));
		for(auto [node2, queryIndex]: queries[node]){
			auto const iterator=curSet.lower_bound(lessFirst[node2]);
			if(iterator!=curSet.end() and *iterator<lessLast[node2])
				result[queryIndex]=1;
		}
		return curSet;
	};
	for(auto it: greaterAdd[N]) work(work, it);

	return result;
}

Compilation message (stderr)

werewolf.cpp: In lambda function:
werewolf.cpp:53:17: warning: unused variable 'success' [-Wunused-variable]
   53 |      auto const success=dsu.join(index, other);
      |                 ^~~~~~~
werewolf.cpp: In lambda function:
werewolf.cpp:137:15: warning: unused variable 'success' [-Wunused-variable]
  137 |    auto const success=first.insert(it).second;
      |               ^~~~~~~
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...