/**
题目:
牛客2021-7-26
牛客多校训练营4
F Just a joke
题意:
有一个无向图,现在Alice和Bob轮流操作
每回合每个玩家可以执行以下操作中的一种:
1.选择一条边将其删除
2.选择一个连通分量
(无向图的连通分量是一组节点,使得每对节点通过一条路径连接
图中的其他节点与该集合中的节点不相连。)
先无法操作的输,判断谁赢得了比赛
思路:
这一题如其题目的名字一样,是个joke
这些输入的无向图的边都是用来混淆的
只需要判断边和点的和的奇偶性就可以了
对于第一个操作,删掉一条边,边数-1
对于第二个操作,删掉一个连通分量,点数-k,边数-(k-1)
任何一种操作,都会使点数和边数的和减少一个奇数
第一个操作总共-1
第二个操作,总共-(2*k-1)
所以答案只和n+m的奇偶性有关
每次操作之后,n+m的奇偶性都会发生变化
当n+m时奇数时,可以进行奇数次操作,说明Bob一定输
当n+m是偶数时,可以进行偶数次操作,说明Alice一定输
**/
#include<iostream>
using namespace std;
int main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=m;i++){//没用的输入
int x,y;
cin >> x >> y;
}
//只需判断奇偶性
if((n+m)%2!=0) cout << "Alice" << endl;
else cout << "Bob" << endl;
return 0;
}
/**
题目:
牛客2021-7-26
牛客多校训练营4
J Average
题意:
给出两个数组a,b
w[i][j]=a[i]+b[j]
要求找一个矩阵长宽至少大于x,y的子矩阵的最大平均值
思路:
题目要求求的是sum{i=l1-r1,j=l2-r2}w[i][j]/((r1-l1+1)(r2-l2+1))
根据题意可以知道w[i][j]=a[i]+b[j]
所以sum{i=l1-r1,j=l2-r2}w[i][j]
=sum{i=l1-r1,j=l2-r2}a[i]+b[j]
=sum{i=l1-r1}a[i]+sum{j=l2-r2}b[j]
=(sum{i=l1-r1}a[i])(r2-l2+1)+(sum{i=l2-r2}b[i])(r1-l1+1)
所以要求的平均值即为
(sum{i=l1-r1}a[i])(r1-l1+1)+(sum{i=l2-r2}b[i])(r2-l2+1)
所以答案是分别对a和b对应的区间求个平均值然后加起来
问题即可转化成:
找a的一个长度至少为x的平均值最大的子区间和
b的一个长度至少为y的平均值最大的子区间
设答案为s,对答案进行二分,令a[i]变成a[i]-s
则问题变成求是否有和>=0的长度至少为x的子区间,这个可以O(n)计算
**/
#include<iostream>
using namespace std;
const int N=1e5+5;
double a[N];
double b[N];
double c[N];
bool check(double k,double *a,int n,int x){
//判断当前平均值是否满足要求?
//即判断原数组中是否存在某一段子段的平均值大于等于当前二分到的这个平均值
//那么我们如果给原数组每一项都减去一个当前的平均值
//即判断是否存在一段子段的和大于等于0
//如果该子段和大于0,则当前平均值满足要求
for(int i=1;i<=n;i++){
c[i]=c[i-1]+a[i]-k;//减去当前平均值的数组
}
double ans=-0x3f3f3f3f;//当前最大子段和
double mn=0;//当前最小前缀和初始化为正无穷
for(int i=x;i<=n;i++){//子串长度必须要满足x
ans=max(ans,c[i]-mn);//更新子段和,子段和为当前前缀和减去最小前缀和
mn=min(mn,c[i-x+1]);//更新最小前缀和
}
return ans>0;
}
double cal(double *a,int n,int x){
double l=0;
double r=1e5;
while(r-l>1e-8){
double mid=(l+r)/2;
if(check(mid,a,n,x)){
l=mid;
}
else r=mid;
}
return l;
}
int main()
{
int n,m,x,y;
cin >> n >> m >> x >> y;
for(int i=1;i<=n;i++){
cin >> a[i];
}
for(int i=1;i<=m;i++){
cin >> b[i];
}
printf("%.10lf\n",cal(a,n,x)+cal(b,m,y));
return 0;
}
/**
/**
题目:
杭电2021-7-29
2021"MINIEYE杯"中国大学生算法设计超级联赛(4)
1002 Kanade Loves Maze Designing
题意:
给一颗树,树上每个点编号1-n,每个点有一颜色
现在告诉你每个点的子节点
求从i到j的路径上,颜色的种类个数这样的一个矩阵a
根据f(i,x)=sum{j=1-n}a(i,j)*x^(j-1)这个函数
求出f(i,,19560929)mod(1e9+7)以及
f(i,19560929)mod(1e9+9)
思路:
注意到允许一个0(n^2)的算法,考虑用+1法统计每种颜色即可
即记ci为颜色i,hs[ci]为颜色ci的出现次数,从cur点出发
进入该点时,给ci加一,如果这种颜色第一次出现,则给cnt加一,记录答案
退出该点时,如果给ci减一后这种颜色不会出现,则cnt减一
**/
#include<iostream>
#include<vector>
#include<string.h>
#define int long long
typedef long long ll;
using namespace std;
const int N=2005;
const int mod1=1e9+7;
const int mod2=1e9+9;
vector<int>v[N];
int c[N];
int x1[N];
int x2[N];
int hs[N];
int res1,res2;
int cnt=0;
void dfs(int cur,int father){
if(!hs[c[cur]]){//当前结点的颜色如果还未出现
cnt++;//颜色种类+1
}
hs[c[cur]]++;//这种颜色的数量增加
res1=(res1+cnt*x1[cur])%mod1;//计算
res2=(res2+cnt*x2[cur])%mod2;
for(auto i:v[cur]){//这个点能够到达的子节点
if(father!=i){
dfs(i,cur);
}
}
hs[c[cur]]--;//回溯
if(!hs[c[cur]]){//这个点没有了
cnt--;//颜色种类-1
}
}
void init(){
for(int i=2;i<=2000;i++){
x1[1]=1;
x2[1]=1;
x1[i]=x1[i-1]*19560929%mod1;
x2[i]=x2[i-1]*19560929%mod2;
}
}
signed main()
{
init();
int t;
cin >> t;
while(t--){
int n;
cin >> n;
for(int i=1;i<=n;i++){//初始化
v[i].clear();
}
for(int i=2;i<=n;i++){
int x;
cin >> x;
v[i].push_back(x);
v[x].push_back(i);
}
for(int i=1;i<=n;i++){
cin >> c[i];//权值
}
for(int i=1;i<=n;i++){//每个点进行dfs
memset(hs,0,sizeof(hs));//颜色个数初始化
res1=0;//f1的值
res2=0;//f2的值
dfs(i,0);
cout << res1 << " " << res2 << endl;
}
}
return 0;
}
/**
题目:
牛客2021-7-31
牛客暑假多校训练营5
K King of Range
题意:
给你n个数以及m次询问
每次询问给你一个k
要求子序列的最大值和最小值之差(也就是极差)>k的子序列个数有几个
思路:
令ri为l=i时,满足极差大于k的最小的r,如果不存在则记ri为n+1
那么显然有ri<=r2<=r3<=rn
一个线性的做法:
注意到因为区间端点都是单调的,所以可以维护两个单调对列
其中一个递增序列,队首维护最小值,一个递减序列,队首维护最大值
每次弹出两个队列中队首靠前的一个,直到极差<=k,
那么就可以在均摊O(1)的时间内求ri
**/
#include<iostream>
#include<deque>
#define int long long
typedef long long ll;
using namespace std;
const int N=1e5+5;
int a[N];
int q1[N];
int q2[N];
signed main()
{
int n,m;
cin >> n >> m;
for(int i=1;i<=n;i++){
cin >> a[i];
}
while(m--){
int k;
cin >> k;
int ans=0;
deque<int>ma,mi;//两个双端对列维护最大值和最小值
ma.push_back(a[1]);
mi.push_back(a[1]);
for(int l=1,r=1;l<=n;l++){
while(r<=n && ma.front()-mi.front()<= k){
r++;
if(r>n){
break;
}
while(!ma.empty()&&ma.back()<a[r]){//小的pop
ma.pop_back();
}
while(!mi.empty()&&mi.back()>a[r]){//大的pop
mi.pop_back();
}
mi.push_back(a[r]);
ma.push_back(a[r]);
}
ans+=n-r+1;
if(mi.front()==a[l]){
mi.pop_front();
}
if(ma.front()==a[l]){
ma.pop_front();
}
}
cout << ans << endl;
}
return 0;
}
/**
题目:
牛客2021-7-31
牛客暑假多校训练营5
B Boxes
题意:
有n个盒子,每个盒子里面有一个黑球或者白球
打开一个盒子看球需要花费为wi
可以通过花费c得知所有盒子中没有打开的黑球的数量
问计算出所有颜色的最小成本的数学期望是多少
思路:
首先c最多只用花一次,且可以在最开始花,所以有两种策略:
1.直接全部打开,代价为sumwi
2.将wi升序排序,先花c的代价,剩下的就相当于一个随机01序列从前往后开,
开到一个前缀全是同色的为之,代价为c+sumwi(1-1/(2^(n-1))
**/
#include<iostream>
#include<algorithm>
using namespace std;
const int N=1e5+5;
double w[N];
int main(){
int n;
double c;
cin >> n >> c;
double sum=0;//全部直接打开
for(int i=1;i<=n;i++){
cin >> w[i];
sum=sum+w[i];
}
sort(w+1,w+1+n);//排序
double res=0;
for(int i=1;i<=n;i++){
res=res/2+w[i];
}
printf("%.9lf\n",sum+min(0.0,-res+c));
return 0;
}