F Just a Joke
题意
博弈游戏,给一个无环图,每次可以删除一条边或者无环一个连通分量。
解析
由于删除的是无环连通分量,因此 k k k个点的连通分量必须是 k − 1 k-1 k−1条边,当我们删除点的时候减少的边数和点数为 2 k − 1 2k-1 2k−1。
我们有两种操作,都是减少奇数个元素。
- 减少一条边
- 减少一个连通分量
我们最开始的元素总数为 ( n + m ) = k (n+m)=k (n+m)=k,假设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能消除哪种类型的逆序对呢?
( a i , a j ) ( i < j , a i = a j + 1 ) (a_i,a_j)(i<j,a_i=a_j+1) (ai,aj)(i<j,ai=aj+1)
所以我们只需要在归并求逆序对的时候,利用二分查找找到上述 a i a_i ai的个数,然后通过+1的操作,让这些逆序对消除。最后结果就是原数组的逆序对减去被消除的逆序对的个数。
但是,如果我们对 a i a_i ai进行加1的操作去消除逆序对,那么我们的 a i + 1 a_i+1 ai+1就不能再进行加一了,本题解中称为固定 a i + 1 a_i+1 ai+1,被固定的值就不能带来任何收益,我们将其收益标记为0。因此我们要进行一个取舍,选择收益最大的。
假设 4 4 3 2 ,最优的策略是对3进行+1,而不是对2进行+1,因此我们只需要判断+1后能带来的收益哪个更大,被固定的值就不能带来任何收益,因此将其收益标记为0。
时间复杂度 O ( N l o g N ) O(NlogN) O(NlogN)
#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
题意
构造题,给出 L C S ( s 1 , s 2 ) = a , L C S ( s 2 , s 3 ) = b , L C S ( s 1 , s 3 ) = c LCS(s1,s2)=a,LCS(s2,s3)=b,LCS(s1,s3)=c LCS(s1,s2)=a,LCS(s2,s3)=b,LCS(s1,s3)=c
讨论一下No的情况
先求出LCS最小值 m i mi mi作为公共部分,每一个LCS都减去 m i mi mi,最长重合部分为 a + b + c + m i < = n a+b+c+mi<=n a+b+c+mi<=n(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
题意
两个数组 a [ i ] , b [ j ] a[i],b[j] a[i],b[j]构建矩阵 w [ i ] [ j ] w[i][j] w[i][j],求此矩阵的平均值最大的子矩阵
解析
w
[
r
1
r
2
]
[
l
1
l
2
]
w[r_1~r_2][l_1~l_2]
w[r1 r2][l1 l2]的平均值公式为
∑
i
=
r
1
r
2
∑
j
=
l
1
l
2
w
[
i
]
[
j
]
(
r
2
−
r
1
+
1
)
∗
(
l
2
−
l
1
+
1
)
\frac{\sum_{i=r_1}^{r_2}\sum_{j=l_1}^{l_2} w[i][j]}{(r_2-r_1+1)*(l_2-l_1+1)}
(r2−r1+1)∗(l2−l1+1)∑i=r1r2∑j=l1l2w[i][j]
由于
w
[
i
]
[
j
]
=
a
[
i
]
+
b
[
j
]
w[i][j]=a[i]+b[j]
w[i][j]=a[i]+b[j],所以我们转换一下。
∑
i
=
r
1
r
2
∑
j
=
l
1
l
2
(
a
[
i
]
+
b
[
j
]
)
(
r
2
−
r
1
+
1
)
∗
(
l
2
−
l
1
+
1
)
\frac{\sum_{i=r_1}^{r_2}\sum_{j=l_1}^{l_2}( a[i]+b[j])}{(r_2-r_1+1)*(l_2-l_1+1)}
(r2−r1+1)∗(l2−l1+1)∑i=r1r2∑j=l1l2(a[i]+b[j])
再转换一下
(
l
2
−
l
1
+
1
)
∑
i
=
r
1
r
2
a
[
i
]
+
(
r
2
−
r
1
+
1
)
∑
j
=
l
1
l
2
b
[
j
]
(
r
2
−
r
1
+
1
)
∗
(
l
2
−
l
1
+
1
)
\frac{(l_2-l_1+1)\sum_{i=r_1}^{r_2} a[i]+(r_2-r_1+1)\sum_{j=l_1}^{l_2}b[j]}{(r_2-r_1+1)*(l_2-l_1+1)}
(r2−r1+1)∗(l2−l1+1)(l2−l1+1)∑i=r1r2a[i]+(r2−r1+1)∑j=l1l2b[j]
化简得
∑
i
=
r
1
r
2
a
[
i
]
(
r
2
−
r
1
+
1
)
+
∑
j
=
l
1
l
2
b
[
j
]
(
l
2
−
l
1
+
1
)
\frac{\sum_{i=r_1}^{r_2} a[i]}{(r_2-r_1+1)}+\frac{\sum_{j=l_1}^{l_2} b[j]}{(l_2-l_1+1)}
(r2−r1+1)∑i=r1r2a[i]+(l2−l1+1)∑j=l1l2b[j]
我们观察式子可知,答案为两个序列的子序列最大平均值,因此我们二分,分别求出两个序列的最大平均值,然后相加即可。
#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);
}
心得
遇到这种相加求平均的题,当时一看就是二分+前缀和,但是还是没写出来,因为公式没化简好,所以遇到这种题目先撸几发公式。