F Just a Joke
题意
博弈游戏,给一个无环图,每次可以删除一条边或者一个连通分量。
解析
由于无环连通,因此个点的连通分量必须是条边,当我们删除点的时候减少的边数和点数为 。
我们有两种操作,都是减少奇数个元素。
- 减少一条边
- 减少一个连通分量
我们最开始的元素总数为,假设A先手,使得K减去一个奇数,变为一个偶数,随后B出手,使得这个偶数又变为奇数。因此每一轮次操作后,结果的奇偶性不变,而操作后元素为0才算胜利,因此只要判断元素总和的奇偶性即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main() { int n,m; cin>>n>>m; if((n+m)&1)cout<<"Alice"; else cout<<"Bob"; }
i Inverse Pair
题意
对序列的元素+1或者+0,构造出新的序列,求经过这个操作后,此序列的最小逆序对。
题解一(二分+归并排序
我们对元素+1能消除哪种类型的逆序对呢?
所以我们只需要在归并求逆序对的时候,利用二分查找找到上述的个数,然后通过+1的操作,让这些逆序对消除。最后结果就是原数组的逆序对减去被消除的逆序对的个数。
但是,如果我们对进行加1的操作去消除逆序对,那么我们的就不能再进行加一了,本题解中称为**固定**,被固定的值就不能带来任何收益,我们将其收益标记为0。因此我们要进行一个取舍,选择收益最大的。
假设 4 4 3 2 ,最优的策略是对3进行+1,而不是对2进行+1,因此我们只需要判断+1后能带来的收益哪个更大,被固定的值就不能带来任何收益,因此将其收益标记为0。
时间复杂度
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=200000+5; int a[N]; int temp[N]; long long int ans=0; map<int,ll>mp; void merge_sort(int a[],int l,int r){ if(l>=r)return; int mid=l+r>>1; merge_sort(a,l,mid); merge_sort(a,mid+1,r); int idx=l; int i=l,j=mid+1; while(i<=mid && j<=r){ if(a[i]>a[j]){ ans+=mid-i+1; // i~mid [i,mid]找到大于1的个数 int index=upper_bound(a+i,a+mid+1 ,a[j]+1)-a; int value=index-i; mp[a[j]]+=(value); temp[idx++]=a[j++]; } else{ temp[idx++]=a[i++]; } } while(i<=mid){ temp[idx++]=a[i++]; } while(j<=r){ temp[idx++]=a[j++]; } for(int k=l;k<=r;k++) a[k]=temp[k]; } int main(){ int n ; cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } merge_sort(a,1,n); ll res=0; for(auto i:mp){ if(mp.find(i.first+1)!=mp.end() ){ if(i.second >= mp[i.first+1]){ mp[i.first+1]=0;//固定 res+=i.second; } } else{//不需要进行取舍,直接消除。 res+=i.second; } } cout<<ans-res<<endl; }
题解二(建图
视频讲解的做法。建树状数组
C LCS
题意
构造题,给出
讨论一下No的情况
先求出LCS最小值作为公共部分,每一个LCS都减去,最长重合部分为(a,b,c其中一个已经为0),如果此条件不成立,就无解。
解析
#include<bits/stdc++.h> using namespace std; typedef long long ll; int main(){ int a,b,c,n; cin>>a>>b>>c>>n; int mi=min({a,b,c});//公共部分 a-=mi,b-=mi,c-=mi; if(a+b+c+mi>n){ cout<<"NO"; } else{ string aa(mi,'a'),bb(mi,'a'),cc(mi,'a'); for(int i=1;i<=a;i++){ aa+='b'; bb+='b'; } for(int i=1;i<=b;i++){ bb+='c'; cc+='c'; } for(int i=1;i<=c;i++){ aa+='d'; cc+='d'; } while(aa.length()<n)aa+='x'; while(bb.length()<n)bb+='y'; while(cc.length()<n)cc+='z'; cout<<aa<<endl<<bb<<endl<<cc<<endl; } }
J Average
题意
两个数组构建矩阵,求此矩阵的平均值最大的子矩阵
解析
的平均值公式为
由于,所以我们转换一下。
再转换一下
化简得
我们观察式子可知,答案为两个序列的子序列最大平均值,因此我们二分,分别求出两个序列的最大平均值,然后相加即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; double eps=1e-7; const int N=1e5+5; int a[N],b[N]; double pa[N]; int n,m,x,y; bool check(int *a,int n,double avg,int x){ for(int i=1;i<=n;i++){ pa[i]=pa[i-1]+a[i]-avg; } double mi=0,mx=INT_MIN; for(int i=x;i<=n;i++){ int j=i-x; mi=min(mi,pa[j]); mx=max(mx,pa[i]-mi); } return mx>=0; } double get_ans(int *a,int n,int x){ double l=0,r=1e7; while(r-l>eps){ double mid=(l+r)/2; check(a,n,mid,x)?l=mid:r=mid; } return l; } int main(){ 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]; double ans=get_ans(a,n,x)+get_ans(b,m,y); printf("%.10lf",ans); }