A:Course
B:Sample Game
C:LCS
题意:已知三个字符串长度,和他们两两之间的LCS长度a,b,c,输出满足条件的三个字符串。
思路:构造题。min({a,b,c})肯定是他们两两之间共有的一个前缀,所以给三个字符串加上这么多个'a'后,然后以此根据a,b,c剩下的值去填'b'和'c',全填完后如果有一个字符串的长度大于n,则输出-1。否则继续填字符d或者e或f直至字符串长度等于n。
代码:
#include <bits/stdc++.h> using namespace std; int main() { int a,b,c,n; cin >> a >> b >> c >> n; string s1,s2,s3; int minn=min(a,min(b,c)); a-=minn,b-=minn,c-=minn; for(int i=1;i<=minn;i++) { s1+='a'; s2+='a'; s3+='a'; } if(a>=b&&a>=c) { for(int i=1;i<=a;i++) { s2+='b'; s1+='b'; } if(b==0) for(int i=1;i<=c;i++) { s1+='c'; s3+='c'; } else if(c==0) { for(int i=1;i<=b;i++) { s3+='c';s2+='c'; } } } else if(b>=a&&b>=c) { for(int i=1;i<=b;i++) { s2+='b'; s3+='b'; } if(a==0) for(int i=1;i<=c;i++) { s1+='c'; s3+='c'; } else if(c==0) { for(int i=1;i<=a;i++) { s1+='c';s2+='c'; } } } else if(c>=a&&c>=b) { for(int i=1;i<=c;i++) { s3+='b'; s1+='b'; } if(b==0) for(int i=1;i<=a;i++) { s1+='c'; s2+='c'; } else if(a==0) { for(int i=1;i<=b;i++) { s3+='c';s2+='c'; } } } int len1=s1.length(),len2=s2.length(),len3=s3.length(); if(len1>n||len2>n||len3>n) { cout << "NO" << endl; return 0; } if(len1<n) { for(int i=1;i<=n-len1;i++) s1+='d'; } if(len2<n) { for(int i=1;i<=n-len2;i++) s2+='e'; } if(len3<n) { for(int i=1;i<=n-len3;i++) s3+='f'; } cout << s1 << endl << s2 << endl << s3 << endl; return 0; }
D:Rebuild Tree
E:Tree Xor
F:Just a joke
题意:有n个点的无向图,两个人每次可以选择进行一种操作;1.删掉一条边;2.删掉一个没有环的连通分量。 问最后谁赢。
思路:第一种操作边数-1,第二种操作点数-k且边数-(k-1)。所以每次不管进行那种操作边和点的个数都会减少一个奇数,所以判断一下n+m的奇偶性即可。
代码:
#include <bits/stdc++.h> using namespace std; int main() { int n,m; cin >> n >> m; for(int i=1;i<=n;i++) { int u,v; cin >> u >> v; } if((m+n)%2==0) { cout << "Bob" << endl; } else cout << "Alice" << endl; return 0; }
G:Product
H:Convolution
I:Inverse Pair
题意:给你一个序列a,现在你可以对a序列每个数+1或+0使它变成一个新序列b,序列ci=bi+ai。输出序列c的逆序对数。
思路:首先求出序列a中的逆序对数,然后记录下每个ai加1是否之前已经出现过了,统计这样的ai的个数。因为如果存在,我们令它加1,就减少了一个逆序对的数量。最后二者做差即可。
代码:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const ll N = 2e5 + 5; ll b[N]; struct node{ ll val; ll pos; }a[N]; ll tree[N], c[N]; ll sum, n; int cmp(node a, node b) { return a.val < b.val; } void add(int m, int k) { while (m <= n) { tree[m] += k; m += m & -m; } } ll inquiry(ll n) { ll sum = 0; while (n) { sum += tree[n]; n -= n & -n; } return sum; } int main() { cin >> n; sum = 0; memset(tree, 0, sizeof(tree)); for (int i = 1; i <= n; i++) { cin >> a[i].val; a[i].pos = i; } ll tmp=0; for(int i=1;i<=n;i++) { if(b[a[i].val+1]) tmp++; else b[a[i].val]=1; } for (int i = 1; i <= n; i++) { } sort(a + 1, a + 1 + n, cmp); ll cnt = 1; for (int i = 1; i <= n; i++) { if (i != 1 && a[i].val != a[i - 1].val) cnt++; c[a[i].pos] = cnt; } for (int i = 1; i <= n; i++) { add(c[i], 1); sum += (i - inquiry(c[i])); } cout << sum - tmp << endl; }
J:Average
题意:给你两个序列a和b,矩阵,让你在矩阵w中找一个子矩阵,其中子矩阵的长不得小于x,宽不得小于y且子矩阵的平均值最大。输出该平均值
思路:由题意我们可以写出平均值的式子,又因为w[i][j]=a[i]+b[j],所以子矩阵的和为: ,联立上面两个式子可得平均值最简式为: 。
所以这道题就变成了去求a的长度至少为x的子区间的最大平均值和b的长度至少为y的最大平均值相加即可,poj2018裸题,二分平均数为mid,把原数组中每个数都减去mid,若最大和>0,就是平均数取小了。这就涉及到均值比较的技巧:平均值大小的比较,可以转换为每对对应元素差值之和与0的比较。。
代码:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; double a[100005],b[100005],sum[100005]; double aa[100005],bb[100005],ssum[100005]; int main(){ int n,m,x,y; cin>>n >> m>>x>>y; for(int i=0;i<n;i++) scanf("%lf",&a[i]); for(int i=0;i<m;i++) scanf("%lf",&aa[i]); double eps=1e-6; double l1=-1e6,r1=1e6; while(r1-l1>eps){ double mid=(r1+l1)/2; for(int i=0;i<n;i++) b[i]=a[i]-mid; for(int i=0;i<n;i++) sum[i]=sum[i-1]+b[i]; double ans=-1e10,minval=1e10; for(int i=x-1;i<n;i++){ minval=min(minval,sum[i-x]); ans=max(ans,sum[i]-minval); } if(ans>0) l1=mid; else r1=mid; } double l2=-1e6,r2=1e6; while(r2-l2>eps){ double mid=(r2+l2)/2; for(int i=0;i<m;i++) bb[i]=aa[i]-mid; for(int i=0;i<m;i++) ssum[i]=ssum[i-1]+bb[i]; double ans=-1e10,minval=1e10; for(int i=y-1;i<m;i++){ minval=min(minval,ssum[i-y]); ans=max(ans,ssum[i]-minval); } if(ans>0) l2=mid; else r2=mid; } printf("%.10lf\n",r1+r2); return 0; }