Problem - E - Codeforces
思路:
- 我们把比赛看成K层(0~K-1)的完美 二叉树
- 按照题目的要求,第k层我们需要把(1<<k)+1~(1<<(k+1))的队伍淘汰掉,显然这些队伍输的位置不能在一起。
- 我们讨论第k层输的队伍的方案数
- 首先,我们需要把那些指定在k层这里哪个位置输的位置处理好,如果有两个队伍输在一个位置,无解。
- 处理完后,那些还没有被特别指定哪个队伍属于这个位置的有cnt个,显然对于这些位置,当前只剩下两个空位(一个给这一层这个位置输的人,一个给这一层赢的人),如果赢的人位置没有指定,显然输的人可以任取这两个位置其中一个,如果指定,只能取剩下的一个
- 所以我们也要处理1~(1<<k)这些人如果有指定位置的话,是指定了k层的那些位置,显然,如果他们有人指定相同位置的话,也是无解(这样就没位置给要输的人了,必须严格位置给输的人,而前面这些人显然这里不能输)
- 所以这一层贡献的方案数就是,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
思路:
-
题目求最小,容易想到二分答案
-
我们每次询问答案mid,我们存储前缀i个数时,选择的数和小于mid最多可以选几个。
-
再处理后缀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;
}