Educational Codeforces Round 149 (Rated for Div. 2)

Problem - E - Codeforces

思路:

  1. 我们把比赛看成K层(0~K-1)的完美 二叉树
  2. 按照题目的要求,第k层我们需要把(1<<k)+1~(1<<(k+1))的队伍淘汰掉,显然这些队伍输的位置不能在一起。
  3. 我们讨论第k层输的队伍的方案数
    1. 首先,我们需要把那些指定在k层这里哪个位置输的位置处理好,如果有两个队伍输在一个位置,无解。
    2. 处理完后,那些还没有被特别指定哪个队伍属于这个位置的有cnt个,显然对于这些位置,当前只剩下两个空位(一个给这一层这个位置输的人,一个给这一层赢的人),如果赢的人位置没有指定,显然输的人可以任取这两个位置其中一个,如果指定,只能取剩下的一个
    3. 所以我们也要处理1~(1<<k)这些人如果有指定位置的话,是指定了k层的那些位置,显然,如果他们有人指定相同位置的话,也是无解(这样就没位置给要输的人了,必须严格位置给输的人,而前面这些人显然这里不能输)
    4. 所以这一层贡献的方案数就是A[cnt]*tmp,A为排列,tmp表示这些cnt位置如果有一个是有两个没有指定的空位,tmp*2,否则*1
#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define int              long long
typedef pair<int, int> pii;
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
//double 型memset最大127,最小128
const int INF = 0x3f3f3f3f;         //int型的INF
const ll llINF = 0x3f3f3f3f3f3f3f3f;//ll型的llINF
const double eps=1e-9;
const int N = 1e6 + 10;
const int mod=998244353;

int dp[N];
int a[N],b[N],pre[N];

void mysolve()
{
	int K,n;
	pre[0]=1;
	cin>>K;
	n=1<<K;
	int x;
	for(int i=1; i<=n; ++i)pre[i]=pre[i-1]*i%mod,a[i]=-1;//预处理排列
	for(int i=1; i<=n; ++i)
		{
			cin>>x;
			if(~x)a[x]=i;
		}

	int ans=1,p=(1<<K)-1;
	for(int k=K-1; ~k; --k)
		{
			for(int i=(1<<k)+1; i<=1<<(k+1); ++i)if(~a[i])dp[(p+a[i])>>(K-k)]++;//处理这一层要输的人在哪个指定的位置输
			for(int i=1; i<=(1<<k); ++i)if(~a[i])b[(p+a[i])>>(K-k)]++;//处理这一层前面的人有没有在哪个指定的位置赢
			int cnt=0,tmp=1;
			for(int i=1<<k; i<(1<<(k+1)); ++i)//枚举这一层每一个位置
				{

					if(dp[i]>=2||b[i]>=2)//如果这个位置指定赢的人/输的人>=2,无解
						{
							cout<<0<<endl;
							return;
						}
					else if(!dp[i])cnt++,tmp=tmp*(b[i]?1:2)%mod;//这个位置没有被输的人指定,那么cnt+1,如果这个位置的两个空位都没有被指定哪个位置是赢的人要的,tmp*2
				}
			ans=ans*pre[cnt]%mod*tmp%mod;
		}
	cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
	ll t=1;
	//cin >> t;
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

Problem - F - Codeforces

思路:

  1. 题目求最小,容易想到二分答案

  2. 我们每次询问答案mid,我们存储前缀i个数时,选择的数和小于mid最多可以选几个。

  3. 再处理后缀i个数时。选择的数和小于mid最多可以选几个,显然如果前缀与后缀交点选的最多选的数和大于k,mid有解

#include <bits/stdc++.h>
using namespace std;
#define ll               long long
#define endl             "\n"
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
#define eps 1e-9
typedef pair<int, int> pii;
inline int read(int &x);
//double 型memset最大127,最小128
//---------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------//
const int N = 3e5 + 10;
const int mod = 998244353;
int n,k,a[N];

bool check(ll mid)
{
	priority_queue<int>q;
	ll sum=0;
	vector<int>cnt(n+1);
	for(int i=1; i<=n; ++i)
		{
			sum+=a[i],q.push(a[i]);
			while(sum>mid)
				{
					sum-=q.top();
					q.pop();
				}
			cnt[i]=q.size();//堆维护前i个数最多可以选几个
		}
	q=priority_queue<int>();
	sum=0;
	for(int i=n; i; --i)
		{
			sum+=a[i],q.push(a[i]);
			while(sum>mid)
				{
					sum-=q.top(),q.pop();
				}//维护后n-i+1个数最多可以选几个
			if((int)q.size()+cnt[i-1]>=k)return 1;
		}
	return 0;
}
void mysolve()
{
	cin>>n>>k;
	ll sum=0;
	for(int i=1; i<=n; ++i)cin>>a[i],sum+=a[i];
	ll l=1,r=sum;
	ll ans=sum;
	while(l<=r)//二分答案
		{
			ll mid=l+((r-l)>>1);
			if(check(mid))ans=mid,r=mid-1;
			else l=mid+1;
		}
	cout<<ans<<endl;
}

int32_t main()
{
	std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);//使用read请把解绑注释了
	int t=1;
	cin >> t;
	//read(t);
	while (t--)
		{
			mysolve();
		}
	system("pause");
	return 0;
}

inline int read(int &x)
{
	x = 0;
	char ch = 0;
	while (ch < '0' || ch > '9')ch = getchar();
	while (ch >= '0' && ch <= '9')
		{
			x = x * 10 + ch - '0';
			ch = getchar();
		}
	return x;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值