CF 172题解
CF 172
这里没有E题,因为蒟蒻看不懂E题的TJ
你问为什么是div1?GG刚说完避开div1!
因为这里有我A的第一道黑题,CF280D,虽然她现在降紫了
A题
洛谷链接,CF链接
此题为计算几何题目,需要一些前置芝士。
部分芝士如下:
点\((x,y)\)绕原点旋转\(θ\),求所得点\((x',y')\)
解:设点\((x,y)\)到原点距离为\(r\),点\((x,y)\)与x轴所成角为\(φ\)
则有:
联立下式:
解得:
求线段AB,CD交点
一个精度比较低且不常用的做法:
将两个直线方程联立求解得到交点,代入其中一个直线方程检验是否在线段上
更为常用和科学的做法可以看上方芝士链接,建议从头开始看
求有\(n\)个顶点的多边形的面积
记\(V_x\)为多边形第\(x\)个顶点与某一定点所形成的向量,要求顶点\(x\)与顶点\(x+1\ mod \ n\)为相邻的顶点
则多边形面积\(S=\sum_1^nV_i×V_{i+1\ mod\ n}\)(×为向量叉乘)
本题做法
顺次求出所有线段的交点,为多边形的所有顶点,然后使用上方公式即可
AC code
暂无 后补
B题
单调栈简述
时空复杂度均为\(O(n)\)
性质:单调栈内满足位置和值的双单调
可用于:一个点一侧第一个满足某一信息的点
例如找右侧第一个比他大的点,那么我们维护一个栈,从右向左扫,若栈顶小于当前点i,那么这个点不仅在对于当前点不会成为答案,对于这个点i左侧的点,也不会成为答案(若小于栈顶,一定小于当前点i),这个点有生之年并没有用了,直接弹走就行,直到栈顶大于当前点为止。栈顶就是i右侧第一个大于i的点。
本题做法
此题为数据结构实现高效枚举,需要一定的思维力度。
对于一共只有最大值、次大值二元的问题,我们可以考虑分类讨论他们的位置关系,或者进行一定一动的操作。
我们假设固定最大值,那么次大值选择有很多,并没有前途。
但是我们如果固定次大值,那么最大值只能为其左侧第一个比它大的,或者其右侧第一个比它大的。由此,我们发现,最大值和次大值的二元组的总体数量级在O(2*n)级别,那么高效枚举出所有二元组即可通过此题。
同时向左找和向右找并不好做,但是我们可以考虑先统一做最大值在左侧的,再统一做最大值在右侧的,就变成了对于每个元素,找其左侧的第一个比它大的,和右侧第一个比它大的,然后使用单调栈正着做一遍反着做一遍就做完了。
AC code
#include<bits/stdc++.h>
using namespace std;
const int MAXN =1e5+5;
int n;
int a[MAXN];
int p[MAXN];
int head,tail;
int ans;
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
//freopen("data2.in","r",stdin);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
head=0;
for(int i=1;i<=n;i++){
while(a[p[head]]<a[i]&&head>=1){head--;}
if(head>=1)ans=max(a[i]^a[p[head]],ans);
p[++head]=i;
}
head=0;
for(int i=n;i>=1;i--){
while(a[p[head]]<a[i]&&head>=1){head--;}
if(head>=1)ans=max(a[i]^a[p[head]],ans);
p[++head]=i;
}
cout<<ans<<endl;
return 0;
}
C题
期望的计算
类似于加权平均值,且权和为1的情况。总期望等于各个情况下的答案*这个情况发生的概率。
期望的线性性
公式:期望E(x+y+z+...)=E(x)+E(y)+E[z]+...
文字理解:一些事件和的期望等于这些事件各自期望的和,即和的期望等于期望的和
此题做法
依据期望的线性性,删除整个树的操作期望,等价于分别删除每个点的期望和。一个点本身被删除有两种情况:
- 删除了其本身,概率为1/(祖先个数+1),产生对答案1的贡献
- 删除了其祖先,概率为(祖先个数)/(祖先个数+1),对答案无贡献
祖先个数为dep[i]-1,故每个点的期望为\(1*1/dep[i]+0*(dep[i]-1)/dep[i]\),即\(1/dep[i]\),对于每个点的期望求和即可
AC code
#include<bits/stdc++.h>
using namespace std;
const int MAXN =1e5+5;
struct Edge{
int nx,to;
}edge[2*MAXN];
int head[MAXN];
int dep[MAXN];
int cnt;
int n;
double ans;
void add(int fm,int to){
edge[++cnt].to=to;
edge[cnt].nx=head[fm];
head[fm]=cnt;
}
void dfs(int x,int deep){
//cout<<x<<' '<<deep<<endl;
dep[x]=deep;
ans+=1/(1.0*dep[x]);
for(int i=head[x];i;i=edge[i].nx){
int t=edge[i].to;
if(dep[t])continue;
dfs(t,deep+1);
}
}
int main(){
cin>>n;
int u,v;
for(int i=1;i<n;i++){
cin>>u>>v;
add(u,v);
add(v,u);
}
dfs(1,1);
printf("%.8lf",ans);
return 0;
}
D题
洛谷链接,CF链接
此题为线段树维护反悔贪心。题解中有人称之为模拟费用流思想,但是没学费用流并不影响本题的理解。
题意为:
给定一个序列
1.带修改。
2.查询区间k段不相交子段和的最大值。
此题的前置,需要掌握一个熟知结论,或者说经典例题小白逛公园,题意为:带修改,维护区间最大子段和。
小白逛公园做法简述
直接维护区间最大子段和在线段树上无法合并,因为无法保证左右儿子的最大子段和是连续的。于是我们维护一个必须含有左端点的最大子段和、必须含有右端点的最大子段和,每次合并时,本段的区间最大子段和为【左儿子本身最大子段和/右儿子本身的最大子段和/左儿子含右端点的最大子段和+右儿子必须含左端点的最大子段和】的最大值。
我们发现,合并时,必须含左端点的最大子段和可能是左儿子的必须韩左端点的最大子段和,也可能是左儿子整个区间与右儿子含左端点的最大子段和拼合而成,于是我们也需要维护整个区间和。区间和可以直接合并,无需其他辅助。
本题做法
每次选取当前的最大子段和,并对当前选走的子段取反,再次选到相等于放弃选,即反悔,进行k次,得到的总和就是k段不相交的字段和的最大值,复杂度O(\(m\ k\ log n\)),可以通过此题。
此题坑点:
- 每次操作完成后,记得对于当前取反的所有子段进行取反,使得所有子段面对一次新的询问时都是原子段;
- 取反操作要求维护每个子段的区间位置,即区间最大子段和的左右端点、必须含左端点的左右端点、必须含右端点的左右端点,这样才能准确取反;
- 整个区间取反的同时,新的最大子段和是原先的最小子段和,对于最小子段和,不能暴力维护,这样复杂度会寄,只需要在维护最大子段和的同时建立统一套相同的最小字段和维护即可;
- 此题的check不能直接返回最大子段和,需要返回一个区间信息,所有返回值应该是Node,对于跨过中线的询问,应该用merge函数将左儿子返回区间、右儿子返回区间合并。
经过以上坑点的调整,此题码量较为爆炸。
AC code
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+5;
int a[MAXN];
int n,m;
struct Node{
long long ma,mal,mar,sum,mi,mil,mir;//记录值
int lma,rma,lmal,lmar,rmal,rmar,lmi,rmi,lmil,lmir,rmil,rmir;//记录位置
int tag;
}f[4*MAXN];
queue<pair<int ,int >>q;
Node merge(Node l,Node r){
Node temp;
//temp.sum
temp.sum=l.sum+r.sum;
//temp.ma
if(l.ma>=r.ma&&l.ma>=l.mar+r.mal){
temp.ma=l.ma;
temp.lma=l.lma;
temp.rma=l.rma;
}
else if(r.ma>=l.ma&&r.ma>=l.mar+r.mal){
temp.ma=r.ma;
temp.lma=r.lma;
temp.rma=r.rma;
}
else if(l.mar+r.mal>=l.ma&&l.mar+r.mal>=r.ma){
temp.ma=r.mal+l.mar;
temp.lma=l.lmar;
temp.rma=r.rmal;
}
//temp.mi
if(l.mi<=r.mi&&l.mi<=l.mir+r.mil){
temp.mi=l.mi;
temp.lmi=l.lmi;
temp.rmi=l.rmi;
}
else if(r.mi<=l.mi&&r.mi<=l.mir+r.mil){
temp.mi=r.mi;
temp.rmi=r.rmi;
temp.lmi=r.lmi;
}
else if(l.mir+r.mil<=r.mi&&l.mir+r.mil<=l.mi){
temp.mi=l.mir+r.mil;
temp.lmi=l.lmir;
temp.rmi=r.rmil;
}
//temp.mal
if(l.mal>=l.sum+r.mal){
temp.mal=l.mal;
temp.lmal=l.lmal;
temp.rmal=l.rmal;
}
else if(l.sum+r.mal>=l.mal){
temp.mal=l.sum+r.mal;
temp.lmal=l.lmal;
temp.rmal=r.rmal;
}
//temp.mar
if(r.mar>=r.sum+l.mar){
temp.mar=r.mar;
temp.rmar=r.rmar;
temp.lmar=r.lmar;
}
else if(r.sum+l.mar>=r.mar){
temp.mar=r.sum+l.mar;
temp.rmar=r.rmar;
temp.lmar=l.lmar;
}
//temp.mil
if(l.mil<=l.sum+r.mil){
temp.mil=l.mil;
temp.lmil=l.lmil;
temp.rmil=l.rmil;
}
else if(l.sum+r.mil<=l.mil){
temp.mil=l.sum+r.mil;
temp.lmil=l.lmil;
temp.rmil=r.rmil;
}
//temp.mir
if(r.mir<=r.sum+l.mir){
temp.mir=r.mir;
temp.rmir=r.rmir;
temp.lmir=r.lmir;
}
else if(r.sum+l.mir<=r.mir){
temp.mir=r.sum+l.mir;
temp.rmir=r.rmir;
temp.lmir=l.lmir;
}
temp.tag=0;
//cout<<temp.ma<<' '<<temp.lma<<','<<temp.rma<<" in "<<temp.lmal<<','<<temp.rmar<<' '<<endl;
return temp;
}
void build(int x,int l,int r){
if(l==r){
f[x].ma=f[x].mal=f[x].mar=f[x].sum=f[x].mi=f[x].mil=f[x].mir=a[l];
f[x].lma=f[x].rma=f[x].lmal=f[x].lmar=f[x].rmal=f[x].rmar=f[x].lmi=f[x].rmi=f[x].lmil=f[x].lmir=f[x].rmil=f[x].rmir=l;
f[x].tag=0;
return;}
int mid=(l+r)/2;
build(2*x,l,mid);
build(2*x+1,mid+1,r);
f[x]=merge(f[2*x],f[2*x+1]);
}
void clear(int x){
swap(f[x].ma,f[x].mi);
swap(f[x].lma,f[x].lmi);
swap(f[x].rma,f[x].rmi);
swap(f[x].mal,f[x].mil);
swap(f[x].lmal,f[x].lmil);
swap(f[x].rmal,f[x].rmil);
swap(f[x].mar,f[x].mir);
swap(f[x].rmar,f[x].rmir);
swap(f[x].lmar,f[x].lmir);
f[x].ma*=-1;f[x].mi*=-1;
f[x].mal*=-1;f[x].mil*=-1;
f[x].mar*=-1;f[x].mir*=-1;
f[x].sum*=-1;
f[x].tag^=1;
}
void swa(int x,int l,int r,int a,int b){
//cout<<"swa: "<<l<<' '<<r<<' '<<a<<' '<<b<<endl;
if(l==a&&r==b){
/*if(!cnt[x]){mp[++cn]=x;};
cnt[x]++;*/
clear(x);return ;
}
if(f[x].tag){
clear(2*x);
clear(2*x+1);
f[x].tag^=1;
}
int mid=(l+r)/2;
if(b<=mid){
swa(2*x,l,mid,a,b);
}
else if(a>mid){
swa(2*x+1,mid+1,r,a,b);
}
else{
swa(2*x,l,mid,a,mid);
swa(2*x+1,mid+1,r,mid+1,b);
}
f[x]=merge(f[2*x],f[2*x+1]);
//cout<<f[x].ma<<' '<<f[x].lma<<' '<<f[x].rma<<endl;
/*
swap(f[x].ma,f[x].mi);
swap(f[x].mal,f[x].mil);
swap(f[x].mar,f[x].mir);
f[x].ma*=-1;f[x].mi*=-1;
f[x].mal*=-1;f[x].mil*=-1;
f[x].mar*=-1;f[x].mir*=-1;
*/
}
void pushdown(int x){
if(f[x].tag){//保证合并时左右儿子信息正确
f[x].tag^=1;
clear(2*x);
clear(2*x+1);
}
}
void modify(int x,int l,int r,int pos,int k){
if(l==r){
f[x].sum=f[x].ma=f[x].mal=f[x].mar=f[x].mi=f[x].mil=f[x].mir=a[l]=k;
f[x].lma=f[x].rma=f[x].lmal=f[x].lmar=f[x].rmal=f[x].rmar=f[x].lmi=f[x].rmi=f[x].lmil=f[x].lmir=f[x].rmil=f[x].rmir=pos;
f[x].tag=0;
return ;
}
pushdown(x);
int mid=(l+r)/2;
if(pos<=mid)modify(2*x,l,mid,pos,k);
else if(pos>mid)modify(2*x+1,mid+1,r,pos,k);
f[x]=merge(f[2*x],f[2*x+1]);
}
Node check(int x,int l,int r,int a,int b){
//if(a<b){return ;}
if(l==a&&r==b){
return f[x];
}
pushdown(x);
int mid=(l+r)/2;
if(b<=mid){return check(2*x,l,mid,a,b);}
else if(a>mid){return check(2*x+1,mid+1,r,a,b);}
else return merge(check(2*x,l,mid,a,mid),check(2*x+1,mid+1,r,mid+1,b));
}
void csh(){
int templ,tempr;
while(!q.empty()){
templ=q.front().first;
tempr=q.front().second;
q.pop();
swa(1,1,n,templ,tempr);
}
}
int main(){
//freopen("data.in","r",stdin);
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
cin>>m;
int type,pos,l,r,k,ans;Node temp;
while(m--){
cin>>type;
if(type==0){
cin>>pos>>k;
modify(1,1,n,pos,k);
}
else if(type==1){
cin>>l>>r>>k;
ans=0;
while(k--){
temp=check(1,1,n,l,r);
if(temp.ma<=0){break;}
//cout<<temp.ma<<' '<<temp.lma<<' '<<temp.rma<<endl;
ans+=temp.ma;
swa(1,1,n,temp.lma,temp.rma);
q.push(make_pair(temp.lma,temp.rma));
}
cout<<ans<<endl;
csh();
}
}
return 0;
}
E题
蒟蒻不会

浙公网安备 33010602011771号