F-Just a joke
思路
这题比赛时稀里糊涂就过了,赛后看题解才明白:第一种操作会使边数m-1,第二种操作会使点数n-k,边数m-(k-1),
不管选择哪种操作,都会使(n+m)减少奇数个,因此只需判断原图的(n+m)的奇偶性,若是(n+m)为奇数,则先手胜,反之则后手胜。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int n, m;
vector <int> g[N];
int main()
{
cin >> n >> m;
int u, v;
for(int i=0; i<m; i++)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
if((n+m)%2) cout << "Alice\n";
else cout << "Bob\n";
return 0;
}
I-Inverse Pair
思路
考点:树状数组求逆序对
思路:最后的C数组是A数组每个数加上0或1所得,要使它的逆序对最少,考虑将A数组的哪些数加1后逆序对减少,显然当左边的数比右边大1时,我们把右边的数加1,就能使逆序对数-1。最后的结果即为A数组的逆序对数-符合条件的对数。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5+10;
int n;
int a[N], b[N];
int st[N];
LL c[N];
struct TT
{
int v, w;
bool operator < (const TT& b) const
{
return v < b.v;
}
}t[N];
int lowbit(int k)
{
return k&(-k);
}
void update(int t, int value)
{ //即一开始都为0,一个个往上加(+1),
int i;
for (i = t; i <= n; i += lowbit(i))
c[i] += value;
}
LL getsum(int t)
{ //即就是求和函数,求前面和多少就是小于它的个数
int i;
LL sum = 0;
for (i = t; i >= 1; i -= lowbit(i))
sum += c[i];
return sum;
}
int main()
{
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%d",&a[i]);
t[i] = {a[i], i};
b[a[i]] = i;
}
int cnt = 0;
LL ans = 0;
sort(t+1, t+1+n);
for(int i=1; i<=n; i++)
{
//cout << b[i] << " ";
update(t[i].w, 1);
ans += i-getsum(t[i].w);
}
for(int i=1; i<=n; i++)
{
if(b[a[i]-1] > i)
{
a[b[a[i]-1]] ++;
ans --;
}
}
printf("%lld\n",ans);
return 0;
}
J-Average
思路
考点:二分+前缀和
思路:求区间最大平均值。分别求A数组中长度至少为X的平均值最大的区间和B数组中长度至少为Y的平均值最大的区间,
先枚举一个可能的值S,二分答案S,令A[i] = A[i]-S,将问题转化为判断区间和A[i]~A[j]是否>=0,用前缀和预处理。
代码
//注意精度问题!!!二分边界控制好
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
double a[N], b[N];
double sum[N];
double solve(int n, int m)
{
for(int i=1; i<=n; i++) scanf("%lf",&a[i]);
double l = -1e5, r = 1e5;
while(r - l > 1e-10)
{
double mid = (l+r)/2.0;
for(int i=1; i<=n; i++) b[i] = a[i]-mid;
for(int i=1; i<=n; i++) sum[i] = sum[i-1]+b[i];
double minn = 1e6, maxn = -1e6;
for(int i=m; i<=n; i++)
{
minn = min(minn, sum[i-m]); //不断维护左端最小值
maxn = max(maxn, sum[i]-minn); //相减得到区间和
}
if(maxn >= 0) 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\n",solve(n, x)+solve(m, y));
return 0;
}
C-LCS
思路
考点:构造
思路:假设a>=b>=c,则a+b-c>n无解。先将三个字符串加上c个’a’,然后分别对两个字符串加上相同的字母,不足n的分别用另一字符补足。
代码
#include <bits/stdc++.h>
using namespace std;
int a, b, c, n;
int main()
{
cin >> a >> b >> c >> n;
string s1, s2, s3;
int t = min(min(a, b), c);
if(a+b+c-2*t > n)
{
puts("NO");
return 0;
}
for(int i=0; i<t; i++)
{
s1 += "a";
s2 += "a";
s3 += "a";
}
for(int i=0; i<a-t; i++)
{
s1 += "b";
s2 += "b";
}
for(int i=0; i<b-t; i++)
{
s2 += "c";
s3 += "c";
}
for(int i=0; i<c-t; i++)
{
s1 += "d";
s3 += "d";
}
while(s1.size() < n) s1 += "e";
while(s2.size() < n) s2 += "f";
while(s3.size() < n) s3 += "g";
cout << s1 << endl;
cout << s2 << endl;
cout << s3 << endl;
return 0;
}
总结
这场牛客做的时候思路格外清晰(然而4题签到只签了三道 ),比较遗憾最后C题没出,赛后看了大佬的代码发现思路是一样的,就是细节处理不到位。构造题确实很少做,CF上有很多类似的,后面要针对性多练一练。下一场比赛继续加油!(希望下一场能成功签完到!)