题目目录:F J I C
F题 Just a joke(就是个玩笑)
题目大意:A和B在玩一个游戏,游戏规则如下:开局给出一张有n个结点的图G,在每个回合可以进行如下操作:
1.选择G图中的一条边删除;
2.选择G图中的一个连通元素(包括单个点在内)删除。
A先手进行操作,进行最后一步操作的玩家获胜。
一个无向图的连通元素是指一个点集中每对结点用一条边连接,一个结点连接一条边,并且图中的其他结点没有边连接这个点集中的点。
举例,三个点构成的图,边的集合为{(1,2)(2,3)(1,3)},{1,2,3}是一个连通元素而{1,2}{1,3}不是。
思路:题目给出了点的个数和边的条数,不管是进行操作1还是操作2,减少的点和边的总个数必然是奇数个,所以只要看n-m的奇偶性即可
#include <bits/stdc++.h> using namespace std; int main(){ int n,m; cin>>n>>m; int m0=m; while(m0--){ int u,v; cin>>u>>v; } if((n-m)%2==0) cout<<"Bob"; else cout<<"Alice"; }
J题 Average
题目大意:给出一个n*m的矩阵W和序列a,序列b。W(i,j)=a(i)+b(j),找出满足长≥x,宽≥y大小的平均值最大的子矩阵。输出最大的平均值。
思路:用二分法的思想分别找出序列a,序列b满足长度要求的平均值最大相加即可。二分法的思想是:假设一个可能的数,用数组里的值全部减去这个值,看是否存在一段长度符合要求且和大于0。二分在当l-r满足精度要求时停止。
#include <bits/stdc++.h> using namespace std; const int N=1e5+10; const double eps=1e-7; double a[N],b[N],sum[N]; bool check(double mid,int n,int m){ for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i]-mid; double minn=1e10; for(int i=m;i<=n;i++){ minn=min(minn,sum[i-m]);//保证长度大于等于m if(sum[i]-minn>=0) return true; } return false; } double solve(int n, int m){//求序列长度大于m的子序列平均值最大 for(int i=1;i<=n;i++) scanf("%lf", &a[i]); double l=0,r=100010; while (r-l>eps){//保证精度符合要求 double mid=(l+r)/2; if(check(mid,n,m)) l=mid; else r=mid; } return r; } int main() { int n,m,x,y; scanf("%d %d %d %d",&n,&m,&x,&y); printf("%.10lf", solve(n,x) + solve(m,y)); return 0; }
I题 Inverse Pair
题目大意:给出一个全排列序列a,可以对每个元素进行+1操作,也可以不加。求能获得的最小逆序数。
思路:当一个数x在x+1后面时,把x加1而x+1不变,这样就可以让逆序数减少一个。建一张图,如果x在x+1后则连一条边,最后会得到多条链,一条长度为b的链可以减少b/2个逆序数。
#include <bits/stdc++.h> using namespace std; const int maxn=2e5+7; int tree[maxn],a[maxn],mp[maxn]; int n; void add(int x) { while(x<maxn) { tree[x]+=1; x+=((x)&-(x)); } } int sum(int x) { int sum=0; while(x) { sum+=tree[x]; x-=((x)&-(x)); } return sum; } int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; long ans=0; for(int i=1;i<=n;i++){ ans+=sum(maxn-1)-sum(a[i]); add(a[i]); } for(int i=n;i>0;i--) { if(mp[a[i]-1]) ans-=1; else mp[a[i]]=1; } cout<<ans<<endl; return 0; }
C题 LCS
题目大意:要求构造三个字符串s1,s2,s3,使得它们俩俩公共子串长度为a,b,c。
思路:先构造公共子串长度最小的一段,然后再根据要求补全其他两子串的长度。如果此时构造的三个字符串长度有大于n的,说明不符合要求,输出NO;否则再用不同的字符(如xyz)分别补全三个字符串的长度到n。
#include <bits/stdc++.h> using namespace std; typedef long long LL; int a,b,c,n; int main(){ cin>>a>>b>>c>>n; int m=min(min(a,b),c); string A,B,C; for(int i=1;i<=m;i++) A+='a',B+='a',C+='a'; for(int i=1;i<=a-m;i++) A+='b',B+='b'; for(int i=1;i<=b-m;i++) B+='c',C+='c'; for(int i=1;i<=c-m;i++) A+='d',C+='d'; if(A.size()>n||B.size()>n||C.size()>n){ puts("NO"); return 0; } while(A.size()<n) A+='x'; while(B.size()<n) B+='y'; while(C.size()<n) C+='z'; cout<<A<<endl; cout<<B<<endl; cout<<C<<endl; return 0; }