练习记录-cf-Codeforces Round 881 (Div. 3)A-F2
E是补的 太蠢了没想到
期末考完的复健
A. Sasha and Array Coloring
题意:可以给不同数字涂上很多颜色,每个颜色的贡献是同一个颜色内的数字最大值和最小值的差
思路:排序一遍,取头和尾的差

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; int a[MAXN]; void solve(){ int n;cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; sort(a+1,a+1+n); int l=1,r=n; int sum=0; while(l<=r){ sum+=a[r]-a[l]; r--;l++; } cout<<sum<<"\n"; } signed main(){ int t; cin>>t; while(t--) solve(); }
B. Long Long
题意:可以选某一段区间,改变它的正负性,求整个都是正的情况的改变次数
思路:遇到第一个负的标记,随后遇到第一个正的取消标记,遇到负的再标记 标记次数就是答案

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; #define int long long void solve(){ int n;cin>>n; int sum=0; int cnt=0; int flag=0; for(int i=1;i<=n;i++){ int a;cin>>a; if(a<0&&flag==0) { flag=1; cnt++; } else if(a>0&&flag==1){ flag=0; } if(a<0) sum+=(a*-1); else sum+=a; } cout<<sum<<" "<<cnt<<"\n"; } signed main(){ int t;cin>>t; while(t--) solve(); }
C. Sum in Binary Tree
题意:给你一个点,求它到二分树顶点1的路径和
思路:这个树的性质是 父节点是子节点/2 一直除就行了

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; #define int long long void solve(){ int n;cin>>n; int sum=0; while(n){ sum+=n; n/=2; } cout<<sum<<'\n'; } signed main(){ int t;cin>>t; while(t--) solve(); }
D. Apple Tree
题意:给你一棵树,从某两个节点掉下两个苹果,问调到树的叶子上的可能性组合(有序对)
思路:用树上dfs求出每个节点连通的叶子节点个数,查询时O(1)乘一下就行了

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; #define int ll vector<int> adj[MAXN]; int ans[MAXN]; void dfs1(int u,int fa){ if(u!=1&&adj[u].size()==1) ans[u]=1; for(auto &v:adj[u]){ if(v==fa) continue; dfs1(v,u); ans[u]=ans[u]+ans[v]; } } void solve(){ int n;cin>>n; for(int i=1;i<=n;i++) adj[i].clear(),ans[i]=0; for(int i=1;i<n;i++){ int u,v;cin>>u>>v; adj[u].push_back(v); adj[v].push_back(u); } dfs1(1,-1); int q;cin>>q; while(q--){ int u,v; cin>>u>>v; cout<<ans[u]*ans[v]<<"\n"; } } signed main(){ close; int t;cin>>t; while(t--) solve(); }
E. Tracking Segments
题意:有一个线段,你会按照给定顺序(q)把上面某个位置变成1 给定一些标准子段,你需要求出在第几步变成1操作的时候 刚好有第一个标准字段中的1的个数严格大于长度的1/2
思路:二分答案 给定一个指定步数mid 先标记所有1 我们可以通过前缀和来求出x点前有几个1 于是我们知道此时是否有标准子段满足要求就是遍历所有子段,复杂度为O(m),算其长度和里面1个数即可 因为要找这个分界点 满足二分 写一个二分查找第一个满足的点即可 总复杂度O(mlog(q))

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; #define int ll int n,m,q; struct node{ int l,r; }N[MAXN]; int Q[MAXN]; int a[MAXN]; int pre[MAXN]; bool check(int mid){ int flag=0; for(int i=1;i<=n;i++) a[i]=0,pre[i]=0; for(int i=1;i<=min(mid,q);i++) a[Q[i]]=1; for(int i=1;i<=n;i++){ pre[i]=pre[i-1]+a[i]; } for(int i=1;i<=m;i++){ int l=N[i].l,r=N[i].r; if(pre[r]-pre[l-1]>((r-l+1)/2)) flag=1; } if(flag) return true; else return false; } void solve(){ cin>>n>>m; for(int i=1;i<=m;i++){ cin>>N[i].l>>N[i].r; } cin>>q; for(int i=1;i<=q;i++){ cin>>Q[i]; } int l=1,r=inf; while(l<=r){ int mid=l+r>>1; if(check(mid)) r=mid-1; else l=mid+1; } if(l==inf||r==inf) cout<<"-1\n"; else cout<<l<<"\n"; } signed main(){ close; int t;cin>>t; while(t--) solve(); }
F1. Omsk Metro (simple version)
题意:题目会逐渐构建一棵树,然后询问树上一条路径u-v是否存在点权和为w的一段
f1只会问到树根的
思路:这个思路有点蠢,记录某个点从顶点过来的总的最大值,从上个点出发的最大值(上个点是负的就是0+w), 以及同理最小值 每次添加新点时更新总的Max和带上这个点的Nowmax 以及最小值 查询时看看是否在范围 就行了
//待更新

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 3e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; int a[MAXN]; vector<int> adj[MAXN]; int Min[MAXN],Max[MAXN],Nowmax[MAXN],Nowmin[MAXN]; void solve(){ int n;cin>>n; for(int i=1;i<=n;i++) a[i]=0,Min[i]=0,Max[i]=0,Nowmax[i]=0,Nowmin[i]=0; a[1]=1;Min[1]=0; Max[1]=1; Nowmax[1]=1; Nowmin[1]=0; int cnt=1; while(n--){ char ch;cin>>ch; if(ch=='+'){ cnt++; int u,w;cin>>u>>w; a[cnt]=w; Min[cnt]=min({0,w,Nowmin[u]+w,Min[u]}); Max[cnt]=max({0,w,Nowmax[u]+w,Max[u]}); Nowmax[cnt]=max({w,Nowmax[u]+w,0}); Nowmin[cnt]=min({w,Nowmin[u]+w,0}); } else{ int u,v,w; cin>>u>>v>>w; if(w==0){ cout<<"Yes\n"; continue; } if(w<Min[v]||w>Max[v]) cout<<"No\n"; else cout<<"Yes\n"; } } } signed main(){ close; int t;cin>>t; while(t--) solve(); }
补完啦 看的一个知乎大佬的思路以及感谢很牛的学长给我debug qwq
题意:询问树上一条路径u-v是否存在点权和为w的一段
思路:使用重链剖分解求树上路径和的思路 就是两个节点 低的往上跳一个链,然后把这个点到 链头的信息合并上去。这边统一从树的上端往下端记录,分别判断左还是右。
如图所示 划分成这三条重链(随便画画意思一下) 把左右两边从上到下的链分开 左边的去左边 最后把左边反向和右边相加。方向和相加顺序如图所示
如果中间的是从上到下 就加右边 否则加左边去
(此处感谢学长的方法 我自己的是全反的 很乱)
此处的合并和求和均使用线段树维护,和维护区间最大值同理

#include<bits/stdc++.h> #define close std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0) using namespace std; typedef long long ll; const ll MAXN = 8e5+7; const ll mod =1e9+7; const ll inf =0x3f3f3f3f; const ll INF =0x3f3f3f3f3f3f3f3f; #define int long long vector<int> adj[MAXN]; int f[MAXN],deep[MAXN],hson[MAXN],size[MAXN],dfn[MAXN],rak[MAXN],top[MAXN],cnt=0,w[MAXN]; vector<pair<pair<int,int> ,int> > que; int tree_build(int u,int fa,int dep){ deep[u]=dep; size[u]=1; f[u]=fa; for(auto &v:adj[u]){ if(v==fa)continue; tree_build(v,u,dep+1); size[u]+=size[v]; if(size[v]>size[hson[u]]||hson[u]==-1) hson[u]=v; } } //记录所在链的链顶,重边优先遍历的dfs序和dfs序对应的编号 void tree_decomposition(int u,int t){ top[u]=t; cnt++; dfn[u]=cnt; rak[cnt]=u; if(hson[u]!=-1){ tree_decomposition(hson[u],t); for(auto &v:adj[u]){ if(hson[u]!=v&&v!=f[u]) tree_decomposition(v,v); } } } int a[MAXN]; struct Info { int lmin=0,rmin=0,lmax=0,rmax=0,min=0,max=0,sum=0; }; Info operator + (const Info &a,const Info &b){ Info c; c.sum=a.sum+b.sum; c.lmin=min(a.lmin,a.sum+b.lmin); c.lmax=max(a.lmax,a.sum+b.lmax); c.rmin=min(b.rmin,b.sum+a.rmin); c.rmax=max(b.rmax,b.sum+a.rmax); c.min=min({a.min,a.rmin+b.lmin,b.min}); c.max=max({a.max,a.rmax+b.lmax,b.max}); return c ; } struct node{ int l,r; Info val,lazy; }seg[MAXN<<2]; void up(int id){ seg[id].val=seg[id<<1].val+seg[id<<1|1].val; //类似状态转移方程 可以更改 } void build(int id,int l,int r){//建立节点编号为1,维护区间是l-r seg[id].l=l; seg[id].r=r; if(l==r){ seg[id].val.sum=a[l]; seg[id].val.lmin=seg[id].val.rmin=min(0LL,a[l]); seg[id].val.lmax=seg[id].val.rmax=max(0LL,a[l]); seg[id].val.min=min(0LL,a[l]); seg[id].val.max=max(0LL,a[l]); return; } int mid=l+r>>1; build(id<<1,l,mid); build(id<<1|1,mid+1,r); up(id);//等处理结束后更新节点信息 } Info query(int id,int ql,int qr){ int l=seg[id].l; int r=seg[id].r; if(ql<=l&&r<=qr) return seg[id].val; int mid=l+r>>1; if(qr<=mid) return query(id<<1,ql,qr);//只在左儿子 else if(ql>mid) return query(id<<1|1,ql,qr);//只在右儿子 else return query(id<<1,ql,qr)+query(id<<1|1,ql,qr); } Info rev(Info a){ Info b=a; swap(b.lmax,b.rmax); swap(b.lmin,b.rmin); return b; } void solve(){ int n;cin>>n; int nowCnt=1; w[1]=1; while(n--){ char ch;cin>>ch; if(ch=='+'){ int fa,value; cin>>fa>>value; nowCnt++; adj[fa].push_back(nowCnt); adj[nowCnt].push_back(fa); w[nowCnt]=value; } else{ int u,v,w; cin>>u>>v>>w; que.push_back({{u,v},w}); } } for(int i=1;i<=nowCnt;i++) hson[i]=-1; tree_build(1,1,1); tree_decomposition(1,1); for(int i=1;i<=nowCnt;i++){ a[dfn[i]]=w[i]; } build(1,1,nowCnt); for(auto it:que){ int u=it.first.first; int v=it.first.second; int value=it.second; Info answer;//ans是最后的信息合并 if(deep[u]<deep[v]) swap(u,v); Info left,right; while(top[u]!=top[v]){//跳重链 if(deep[top[u]]>deep[top[v]]){ left=query(1,min(dfn[top[u]],dfn[u]),max(dfn[top[u]],dfn[u]))+left; u=f[top[u]]; } else{ right=query(1,min(dfn[top[v]],dfn[v]),max(dfn[top[v]],dfn[v]))+right; v=f[top[v]]; } } if(dfn[u]<=dfn[v]) right=query(1,min(dfn[u],dfn[v]),max(dfn[u],dfn[v]))+right; else left=query(1,min(dfn[u],dfn[v]),max(dfn[u],dfn[v]))+left; answer = rev(left)+right; if(value>=answer.min&&value<=answer.max) cout<<"YES\n"; else cout<<"NO\n"; } for(int i=1;i<=nowCnt;i++){ adj[i].clear(); f[i]=0,deep[i]=0,hson[i]=-1,size[i]=0,dfn[i]=0,rak[i]=0,top[i]=0,cnt=0,w[i]=0; } cnt=0; que.clear(); //111 } signed main(){ close; int t;cin>>t; while(t--) solve(); }