D、小红的中位数
D-小红的中位数_牛客周赛 Round 29 (nowcoder.com)
分情况讨论或者二分都行,分类讨论按照n的奇偶性质讨论即可。
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
#define PII pair<int,int>
const int N=1e6+10;
const int INF=1e18;
double arr[N],brr[N];
int n;
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>arr[i],brr[i]=arr[i];
sort(brr+1,brr+1+n);
if(n%2==1)
{
int mid=(n+1)/2;
for(int i=1;i<=n;i++)
{
if(arr[i]<brr[mid])cout<<fixed<<setprecision(1)<<(brr[mid+1]+brr[mid])/2<<endl;
else if(arr[i]>brr[mid])cout<<fixed<<setprecision(1)<<(brr[mid-1]+brr[mid])/2<<endl;
else
{
if(brr[mid-1]!=brr[mid]&&brr[mid+1]!=brr[mid])cout<<fixed<<setprecision(1)<<(brr[mid-1]+brr[mid+1])/2<<endl;
else if(brr[mid-1]==brr[mid])cout<<fixed<<setprecision(1)<<(brr[mid]+brr[mid+1])/2<<endl;
else cout<<fixed<<setprecision(1)<<(brr[mid-1]+brr[mid])/2<<endl;
}
}
}
else
{
int lmid=n/2,rmid=lmid+1;
for(int i=1;i<=n;i++)
{
if(arr[i]<=brr[lmid])cout<<fixed<<setprecision(1)<<brr[rmid]<<endl;
else if(arr[i]>=brr[rmid])cout<<fixed<<setprecision(1)<<brr[lmid]<<endl;
}
}
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
E、小红构造数组
E-小红构造数组_牛客周赛 Round 29 (nowcoder.com)
思路一:先用分解质因数将x所有的质因数以及其个数统计出来,可以证明每次反复选择个数最多的两个质数循环下去可以保证构造出来的数列相邻之间不相等,如果构造不出来,那么输出-1。这里我们可以用一个优先队列来维护个数最大的两个质数。
思路二:可以利用众数的性质,如果众数个数大于其余数的个数,那么最终一定无法构造相邻不相等,否则一定能够构造出来。按频率(从大到小)优先填充偶数位(0-index),然后在填充奇数位,这样天然保证相同元素被隔离,可以反证,证明该思路,一定是OK的。
这里只展示思路一的代码了:
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
#define PII pair<int,int>
const int N=1e6+10;
const int INF=1e18;
priority_queue<PII,vector<PII>,less<PII> >r;
void solve()
{
int x;
cin>>x;
if(x==1)
{
cout<<-1<<endl;
return ;
}
unordered_map<int,int>book;
for(int i=2;i<=x/i;i++)
{
while(x%i==0)
{
x/=i;
book[i]++;
}
}
if(x-1)book[x]++;
for(auto i:book)r.push({i.second,i.first});
vector<int>ans;
while(r.size())
{
auto t=r.top();
r.pop();
ans.push_back(t.second);
if(r.size()==0)
{
if(t.first-1>0)
{
cout<<-1<<endl;
return ;
}
else if(t.first-1==0)
{
break;
}
}
auto q=r.top();
r.pop();
ans.push_back(q.second);
if(t.first-1)r.push({t.first-1,t.second});
if(q.first-1)r.push({q.first-1,q.second});
}
cout<<(int)ans.size()<<endl;
for(int i=0;i<ans.size();i++)cout<<ans[i]<<" ";
cout<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
F、小红又战小紫
F-小红又战小紫_牛客周赛 Round 29 (nowcoder.com)
这个题目出的很好,有借鉴价值,这里有一个点,就是状态压缩,由于有1000个位置,若是每个位置都存一个维度明显无法实现,由于发现其实对于每个人操作后是否会胜利和输赢只和石头个数为2的堆的数量有关,此时可以将n个维度转化为两个维度来表示,一个是石头的堆的总数量,一个是个数为2的堆的数量,然后这题就转化为一个2维的简单概率dp去做了。
dp[i][j]表示一共 i 堆石头,石头个数为2的堆的数量为 j 时先手胜利的概率,
根据概率dp常用模板:f(i)=∑p(i)*f(v[i]);
注意:(当i,j都不为0时,也就是石子个数为2的堆数不为0时,只能采用第一个策略取石子,因为若是先手采用第二个策略,后手可以之间拿完,那么必输)
状态转移方程为:
含义:先手 i, j 状态下赢的概率等于后手 i-1 ,j或者i,j-1输的概率,输的概率即为(1-赢的概率)。
由于要算分数,故而要求逆元。所以逆元下状态转移方程为:
其中inv表示 i 的乘法逆元。
代码如下,这里采用递归加记忆化的写法。
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define PII pair<int,int>
#define int long long
const int N=1e6+10;
const int INF=1e18;
const int mod=1e9+7;
int dp[1010][1010];
int q_pow(int a,int b)
{
if(b==0)return 1;
int temp=q_pow(a,b/2);
if(b%2==0)return temp*temp%mod;
else return temp*temp%mod*a%mod;
}
int pro(int n,int sum)
{
if(dp[n][sum]+1)return dp[n][sum];
if(n==0)return dp[n][sum]=0;
else if(sum==0)return dp[n][sum]=1;
if(n>sum)return dp[n][sum]=(1-pro(n,sum-1))*sum%mod*q_pow(n,mod-2)%mod+(1-pro(n-1,sum))*(n-sum)%mod*q_pow(n,mod-2)%mod;
return dp[n][sum]=1-pro(n,sum-1);
}
int n,sum;
void solve()
{
cin>>n;
memset(dp,-1,sizeof dp);
for(int i=1;i<=n;i++)
{
int x;
cin>>x;
if(x==2)sum++;
}
cout<<(pro(n,sum)%mod+mod)%mod<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}