AtCoder Beginner Contest 236(D,E,F)
AtCoder Beginner Contest 236(D,E,F)
D
这道题的大意是有\(2n\)个数,我们可以让\(i\)和\(j\)配对,且\(i<j\),那么这一对的值就是\(a[i] [j]\),他会给出所有可能出现的配对的方式的值,求出\(n\)对数得到的值的异或值最大的那一个
我之前是想过暴力,因为这个\(n\)小于\(8\),但是我的方向错了,我之前认为每一个数都有\(2n\)个可以和它配对,但是就以为是\({2n}^{2n}\),我就觉得会不会太大了,然后就没有深想了,现在想来真是太蠢了,脑子不太清醒,虽然方向不对,但是这个时间复杂度的预估也太离谱了,简直了
对于每一对,其实我们不用考虑那么多,其实总共就只有\(n\)对,所以较小的那一个,那么那个\(i\)我们其实可以顺其自然,后面那一个才是我们需要找的,这里可以用回溯的方式找出最合适的
这次确实是我想的太过于粗暴了,我以为每一个数要找一个和这个数匹配的数,我如果暴力的话(在不管大小的情况下)有\((n-1)^n\)种情况,就觉得这样就会超时
其实如果我们考虑的大小的关系,如果现在此时需要那个较小的数,那么此时还没有用到的最小的那一个一定是那个\(i\),所以我们其实可以确定此时的\(i\),然后再考虑比\(i\)大的数作为\(j\),这样处理就会使得时间复杂度降低
#include <iostream>
#include <vector>
using namespace std;
const int maxn=30;
#define int long long
int g[maxn][maxn];
bool vis[maxn];
int ans,n;
void dfs(int cur,int cnt,int res)
{
if (cnt==2*n)
{
ans=max(res,ans);
return ;
}
if (cnt%2==0)
{
for (int i=1;i<=2*n;i++)
{
if (vis[i]) continue;
vis[i]=true;
dfs(i,cnt+1,res);
vis[i]=false;
return ;
}
}
else
{
for (int i=cur+1;i<=2*n;i++)
{
if (vis[i]) continue;
vis[i]=true;
int u=cur,v=i;
dfs(i,cnt+1,res^g[u][v]);
vis[i]=false;
}
}
return ;
}
signed main ()
{
cin>>n;
for (int i=1;i<=2*n;i++)
{
for (int j=i+1;j<=2*n;j++)
{
cin>>g[i][j];
}
}
ans=0;
vis[1]=true;
dfs(1,1,0);
cout<<ans<<'\n';
system ("pause");
return 0;
}
E
这个题大意是给你\(n\)个数,我们可以从这\(n\)个数选择一些数,但是对于\(1<=i<n\),对于这里面的每一个\(i\),我们都必须从\(i\)和\(i+1\)的数至少选一个,然后我们需要满足上面条件的若干个数的最大中位数,最大平均值,这两个值的若干个数的选择是相互独立的
我之前想到好多种变化,这是我们自己构造不出来的(我构造不出来)
然后看了一些题解,万万没想到这个竟然可以用二分来写(我都没有想到这方面上)
那么二分的\(check\)函数应该怎么写呢
对于平均数,我们可以知道对于目前已经选择的数的和大于等于此时的\(x\)
这里我们要怎么选择呢
我们这里还定义了一个二维的\(f\)数组,\(f[i] [0]\)代表着从\(1到i\)并且我们不会选择\(a_i\)的情况,\(f[i] [1]\)代表着从\(1到i\)并且我们一定选择\(a_i\)的情况
那么我们就可以选择一下的转移方式
如果我们此时决定\(a_i\)的选择
如果不选,\(f[i] [0]=f[i-1] [1]\),那么它的前一位必须要选择
如果选,\(f[i] [1]=max(f[i-1] [1],f[i-1] [0])+a[i]\),那么它的前一位可选可不选
对于中位数,我们可以知道不比中位数小的数量要小于比中位数小的数多
具体的细节看代码
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=1e5+10;
#define int long long
#define eps 1e-6
int a[maxn],n;
bool check1(double x)
{
vector<vector<double>>f(n+1,vector<double>(2));
f[0][0]=f[0][1]=0;
for (int i=1;i<=n;i++)
{
f[i][0]=f[i-1][1];
f[i][1]=max(f[i-1][1],f[i-1][0])+a[i]-x;
}
return max(f[n][0],f[n][1])>=0;
}
bool check2(int x)
{
vector<vector<int>>f(n+1,vector<int>(2));
for (int i=1;i<=n;i++)
{
f[i][0]=f[i-1][1];
f[i][1]=max(f[i-1][0],f[i-1][1])+(a[i]>=x?1:-1);
}
return max(f[n][0],f[n][1])>0;
}
signed main ()
{
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>a[i];
}
double l=0,r=1e9;
double ave=0;
while (r-l>=eps)
{
double mid=(l+r)/2;
if (check1(mid))
{
l=mid;
ave=mid;
}
else r=mid;
}
int L=0,R=1e9;
int midans=0;
while (L<=R)
{
int mid=(L+R)>>1;
if (check2(mid))
{
midans=mid;
L=mid+1;
}
else R=mid-1;
}
cout<<ave<<'\n'<<midans<<'\n';
system ("pause");
return 0;
}
F
这个题大意是有\(2^n -1\)个数,对于\(1\)到\(2^n -1\)个数,我们如果需要从这些数中选择若干个数,让我们可以得到这若干个数的异或值可以变成\(1,2,3,4,...2……2^n -1\),如果我们要选择\(i\),那么会消耗\(a_i\)的价值,然后我们需要如果有若干个数的异或值可以变成\(1\)到\(2^n -1\)的最小价值
这个是线性基的模板题
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=2e5+10;
#define int long long
int n,m;
signed main ()
{
cin>>n;
m=1<<n;
vector<pair<int,int>>v;
for (int i=1;i<m;i++)
{
int x;
cin>>x;
v.push_back({x,i});
}
sort(v.begin(),v.end());
int ans=0;
vector<int>t(n+1,0);
for (auto [val,x]:v)
{
for (int i=0;i<n;i++)
{
if ((x>>i&1))
{
if (!t[i])
{
ans+=val;
t[i]=x;
break;
}
else
{
x^=t[i];
}
}
}
}
cout<<ans<<'\n';
system ("pause");
return 0;
}

浙公网安备 33010602011771号