Educational Codeforces Round 113 (Rated for Div. 2) E

题目E:有2^{k}个队伍进行比赛,编号为1,2,.....2^{k},要打2^{k}-1场比赛,第一轮编号为奇数的队伍和与它相邻的下一个队伍进行比赛,比赛胜出的2^{k-1}组队伍进行下一轮比赛,之后1vs2胜出的队伍与3vs4胜出的队伍进行比赛,以此类推。然后胜出的队伍继续进行比赛,直到只剩下一组队伍。之后给队伍排名次,第i个小队的名次为pi。比如k为3,则有8个队伍比赛,第一轮比赛为1vs2,3vs4,5vs6,7vs8。若2、4、5、8胜出,则第二轮为2vs4,5vs8,若4、5胜出,则第三轮4vs5。若5胜出,则1、3、6、7名次为5,2、8为3,4为2,5为1。

 定义h=\left ( \sum_{i=1}^{2^{k}}i*A^{p_{i}} \right )%mod,mod=998244354。输入k,A,h,输出各小组排名,如果无法找出合理排名输出-1。

题解:对于每一场比赛,胜负状态有两种,分别为编号小的胜利和编号大的胜利。因此可以用一个2^{k-1}位二进制数表示每场比赛的状况,共2^{2^{k-1}}-1种比赛状态。可以根据比赛的状况求出每个小队的排名,并求出h^{'},若h^{'}=h则输出并且返回。 如果k=5,这样做不能通过此题。此时需要进行双向搜索。当k=5时第一名编号可能在1到2^{k-1}之间,或者在2^{k-1}+12^{k}之间。进行分情况讨论。对于每种情况对编号在前半部分的队伍枚举2^{2^{k-2}}-1种比赛状态,对每种状态的h^{'}值进行标记。然后计算后半部分每种比赛状态的h^{''}值。如果(h-h^{''}+mod)%mod有标记,则说明该种情况可行。
 

#include<bits/stdc++.h>
using namespace std;
const int MOD=998244353;
int add(int x,int y) {//加法取模 
	x+=y;
	while(x>=MOD)x-=MOD;
	while(x<0)x+=MOD;
	return x;
}
int mul(int x,int y) {//乘法取模 
	return (x*1ll*y)%MOD;
}
int binpow(int x,int y) {//x^y取模 
	int z=1;
	while(y) {
		if(y&1)z=mul(z,x);
		x=mul(x,x);
		y>>=1;
	}
	return z;
}
vector<int> evaluate(int n,int choice_mask) {//将状态转化为名次 
	int cur_place=n/2+1;
	int cur_bit=n-2;
	vector<int> p(n);
	vector<int> c(n);
	for(int i=0; i<n; i++)c[i]=i;
	while(c.size()!=1) {
		vector<int> nc;
		for(int i=0; i<c.size(); i+=2) {
			if(choice_mask&(1<<cur_bit)) {
				p[c[i]]=cur_place;
				nc.push_back(c[i+1]);
			} else {
				p[c[i+1]]=cur_place;
				nc.push_back(c[i]);
			}
			cur_bit--;
		}
		c=nc;
		cur_place/=2;
		cur_place++;
	}
	p[c[0]]=1;
	return p;
}
vector<int> adjust(int n,vector<int> p,bool winning) {//调整名次 
	for(int i=0; i<n; i++) {
		if(p[i]==1) {
			if(!winning)p[i]++;
		} else p[i]=p[i]*2-1;
	}
	return p;
}
int get_hash(int n,vector<int> p,int A,bool partial=false,bool winning=false,int shift=0) {//获得对应的h 
	if(partial)p=adjust(n,p,winning);
	int res=0;
	for(int i=0; i<n; i++)
		res=add(res,mul(add(i+1,shift),binpow(A,p[i])));
	return res;
}
int main() {
	int k,A,h;
	cin>>k>>A>>h;
	if(k<=4) {//k<=4直接暴力 
		for(int i=0; i<(1<<((1<<k)-1)); i++) {//每一个i代表一种对局状态 
			vector<int> p=evaluate(1<<k,i);
			if(get_hash(1<<k,p,A)==h) {
				for(auto x:p)cout<<x<<" ";
				cout<<endl;
				return 0;
			}
		}
	} else {
		int mask_left=-1;
		int mask_right=-1;
		bool left_win=false;
		for(int c=0; c<2; c++) {//c==0前半部分产生第一,c==1后半部分产生第一 
			map<int,int> left_map;
			for(int i=0; i<(1<<16); i++) {//将前半部分得分存在left_map中 
				vector<int> p=evaluate(16,i);
				int left_hash=get_hash(16,p,A,true,c==0,0);
				left_map[left_hash]=i;
			}
			for(int i=0; i<(1<<16); i++) {//计算后半部分哈希值并且查询left_map中是否存在使得答案正确的前半部分的排列。 
				vector<int> p=evaluate(16,i);
				int right_hash=get_hash(16,p,A,true,c==1,16);
				int left_hash=add(h,MOD-right_hash);
				if(left_map.count(left_hash)) {
					mask_left=left_map[left_hash];
					mask_right=i;
					left_win=(c==0);
				}
			}
		}
		if(mask_left!=-1) { 
			vector<int> ans_left=evaluate(16,mask_left);
			vector<int> ans_right=evaluate(16,mask_right);
			ans_left=adjust(16,ans_left,left_win);
			ans_right=adjust(16,ans_right,!left_win);
			for(auto x:ans_left)cout<<x<<" ";
			for(auto x:ans_right)cout<<x<<" ";
			return 0;
		}
	}
	cout<<-1<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值