Codeforces Round 905 Div 1 (CF1887)

2023-10-23 12:20
文章标签 codeforces round div 905 cf1887

本文主要是介绍Codeforces Round 905 Div 1 (CF1887),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

A1. Dances (Easy version)

\(a,b\) 序列都从小到大排序,\(a\) 贪心删大的,\(b\) 贪心删小的,二分答案并 \(O(n)\) \(\text{check}\)

Code ```cpp const int N=1e5+5; int T,n,m; int a[N],b[N]; il bool check(int mid) { for(int i=1,j=mid+1;i<=n-mid;i++,j++) if(a[i]>=b[j]) return 0; return 1; } int main() { T=read(); while(T--) { n=read(),m=read(); a[1]=1; for(int i=2;i<=n;i++) a[i]=read(); for(int i=1;i<=n;i++) b[i]=read(); sort(a+1,a+n+1),sort(b+1,b+n+1); int l=0,r=n; while(l >1; if(check(mid)) r=mid; else l=mid+1; } printf("%d\n",l); } return 0; } ```

A2. Dances (Hard Version)

日常给大家提供乐子。

Solution 1

现在的限制变成了 \(m\le 10^9\),肯定不能一个一个求答案。但是发现我们只关心这个 \(a_1\)\(a,b\) 中其余的每个元素的大小关系。也就是说我们只要枚举 \(4\times 10^5\) 种大小关系,对他们分别求解就可以得到所有答案了。
原来对一个给定序列求答案的时间复杂度是 \(O(n\log n)\),考虑优化这个过程。

同样二分删掉的数量 \(mid\),设 \(a_1=x\)\(a\) 序列排序时不包含 \(a_1\)
那么把 \(x\) 插进 \(a\) 的对应位置,需要判断的区间可以分成 \(3\) 段:\(a\) 序列中在 \(x\) 前的部分,\(x\) 的位置,\(a\) 序列中在 \(x\) 后的部分。

已经有两个 \(\log\) 了,需要 \(O(1)\) 地判断 \([a_l,a_r]\)\([b_L,b_R]\) 对应起来是否合法。
\(lst_i\) 表示第一个比 \(b_i\) 小的位置与当前位置的差值。换句话说,取最大的 \(j\) 使 \(a_j<b_i\),令 \(lst_i= j-i\)。那么 \([a_l,a_r]\)\([b_L,b_R]\) 合法的判断条件就是 \(L-l\le \min\{lst_L,\dots,lst_R\}\)。使用 ST 表维护。

时间复杂度 \(O(n \log^2 n)\)
我也不知道我怎么 5min 想到这个做法调了 1h 的,所以写出来给大家看看乐子。

Solution 2

发现答案只有两种,我们先把 \(a_{2,\dots,n}\)\(b\) 匹配。
现在加了一个数,要么以上匹配还是合法,要么变不合法了,那就把新加的那个删掉,答案增加 \(1\)。于是就做完了。

Code ``` #define int long long const int N=2e5+5; int T,n,m; int a[N],b[N],d[N],L[N],tot; int st[20][N]; il bool check(int l,int r,int L,int R) { if(l>r) return 1; int len=__lg(R-L+1); int mn=min(st[len][L],st[len][R-(1< n-mid&&mid) return check(1,n-mid,mid+1,n); bool flag1=check(1,pos-1,mid+1,mid+pos-1); bool flag2=x >1; if(Check(mid,x)) r=mid; else l=mid+1; } return l; } void solve() { tot=1; n=read(),m=read(); d[1]=m; d[++tot]=1; for(int i=1;i 1) ans+=(d[i+1]-d[i]-1)*Solve(d[i]+1); } printf("%lld\n",ans); } signed main() { int T=read(); while(T--) solve(); } ```

B. Time Travel

因为时间和时间不好区分,我们不妨令 \(a_i\) 叫时间,\(i\) 叫日期。
\(dis_i\) 表示到达点 \(i\) 的最早日期。对每条边记录它所属的时间。
把每个 \(i\)\(a_i\) 分组,就可以二分出在当前 \(dis_u\)\(u\to v\) 这条边可通行的最早日期。
把这个当作边权跑 dijkstra 就行了,双 \(\log\) 但是跑得挺快。

Code
const int N=2e5+5,inf=1e9;
#define pii pair<int,int>
#define fi first
#define se second
int n,t,k,a[N];
map<pii,int> mp;
struct edge{int nxt,to,id;} e[N<<1];
int head[N],cnt=1;
il void add(int u,int v,int id) {e[++cnt]={head[u],v,id};head[u]=cnt;}
vector<int> tim[N],nxt[N];
int dis[N];
il int get(int nowt,int id)
{auto ans=upper_bound(nxt[id].begin(),nxt[id].end(),nowt);if(ans==nxt[id].end()) return inf;else return *ans;
}
int main()
{n=read(),t=read();for(int i=1;i<=t;i++){int x=read();for(int j=1;j<=x;j++){int u=read(),v=read();add(u,v,i),add(v,u,i);}}k=read();for(int i=1;i<=k;i++) a[i]=read(),nxt[a[i]].push_back(i);priority_queue<pii,vector<pii>,greater<pii> >q;for(int i=1;i<=n;i++) dis[i]=inf;dis[1]=0,q.push(pii(0,1));while(!q.empty()){int u=q.top().se,f=q.top().fi; q.pop();if(dis[u]!=f) continue;for(int i=head[u];i;i=e[i].nxt){int v=e[i].to,id=e[i].id;int w=get(dis[u],id);if(dis[v]>w) dis[v]=w,q.push(pii(dis[v],v));}}if(dis[n]==inf) printf("-1\n");else printf("%d\n",dis[n]);return 0;
}

C. Minimum Array

被 A2 卡没时间了 /oh

Description

给一个长度为 \(n\) 的初始序列 \(a\)\(q\) 次操作。每次操作形如给 \(a_l\sim a_r\) 的值加上 \(k\)
依次进行这些操作,求过程中得到过字典序最小的序列。
$n,q\le 5\times 105, -109\le a_i,k\le 10^9 $。

Solution 1

考虑依次进行每个操作,维护当前能使答案字典序最小的操作编号 \(ans\)

只考虑从上一个 \(ans\) 到当前时刻的操作,设它们操作后形成的序列为 \(b\)。那么当前的实际序列就是 \([1,ans]\) 的实际序列与 \(b\) 数组对应位相加后的结果。
当前序列比 \([1,ans]\) 字典序更小的充要条件是 \(b\) 中第一个不为 \(0\) 的位置的值 \(<0\),证明是显然的。每次修改后判断,满足这个条件就更新 \(ans\),并清零 \(b\) 数组。

也就是说我们只需要一个数据结构支持区间加、求第一个不为 \(0\) 的位置、单点求值。
直接上线段树是可做的。不过发现第一个不为 \(0\) 的位置只能是某个 \(l_i\)\(r_i+1\),所以把这些点塞进 set 里暴力判断即可,区间加用树状数组维护。

Solution 2

看了下官方题解。
我们直接在差分数组上考虑这个问题,修改变成了单点,更新答案还是求第一个不为 \(0\) 的位置。这样就不用写树状数组了。

Code ``` #define int long long const int N=5e5+5,inf=1e9; int t,n,a[N]; struct node{int l,r,k;} q[N]; struct BIT { int tr[N]; il void modify(int x,int k) {for(;x<=n;x+=x&(-x)) tr[x]+=k;} il void add(int l,int r,int k) {modify(l,k),modify(r+1,-k);} il int query(int x) {int res=0;for(;x;x-=x&(-x)) res+=tr[x];return res;} }tr; int ans; signed main() { int T=read(); while(T--) { n=read(); ans=0; for(int i=1;i<=n;i++) a[i]=read(),tr.tr[i]=0; t=read(); set st; for(int i=1;i<=t;i++) q[i].l=read(),q[i].r=read(),q[i].k=read(); for(int i=1;i<=t;i++) { tr.add(q[i].l,q[i].r,q[i].k); st.insert(q[i].l),st.insert(q[i].r+1); while(st.size()&&!tr.query(*st.begin())) st.erase(st.begin()); if(!st.empty()&&tr.query(*st.begin())<0) { for(int j=i;j>ans;j--) tr.add(q[j].l,q[j].r,-q[j].k); ans=i,st.clear(); } } for(int i=1;i<=n;i++) tr.tr[i]=0; for(int i=1;i<=n;i++) tr.modify(i,a[i]-a[i-1]); for(int i=1;i<=ans;i++) tr.add(q[i].l,q[i].r,q[i].k); for(int i=1;i<=n;i++) printf("%lld ",tr.query(i)); printf("\n"); } } ```

这篇关于Codeforces Round 905 Div 1 (CF1887)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/267791

相关文章

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

Codeforces Round #261 (Div. 2)小记

A  XX注意最后输出满足条件,我也不知道为什么写的这么长。 #define X first#define Y secondvector<pair<int , int> > a ;int can(pair<int , int> c){return -1000 <= c.X && c.X <= 1000&& -1000 <= c.Y && c.Y <= 1000 ;}int m

Codeforces Beta Round #47 C凸包 (最终写法)

题意慢慢看。 typedef long long LL ;int cmp(double x){if(fabs(x) < 1e-8) return 0 ;return x > 0 ? 1 : -1 ;}struct point{double x , y ;point(){}point(double _x , double _y):x(_x) , y(_y){}point op

Codeforces Round #113 (Div. 2) B 判断多边形是否在凸包内

题目点击打开链接 凸多边形A, 多边形B, 判断B是否严格在A内。  注意AB有重点 。  将A,B上的点合在一起求凸包,如果凸包上的点是B的某个点,则B肯定不在A内。 或者说B上的某点在凸包的边上则也说明B不严格在A里面。 这个处理有个巧妙的方法,只需在求凸包的时候, <=  改成< 也就是说凸包一条边上的所有点都重复点都记录在凸包里面了。 另外不能去重点。 int

Codeforces 482B 线段树

求是否存在这样的n个数; m次操作,每次操作就是三个数 l ,r,val          a[l] & a[l+1] &......&a[r] = val 就是区间l---r上的与的值为val 。 也就是意味着区间[L , R] 每个数要执行 | val 操作  最后判断  a[l] & a[l+1] &......&a[r] 是否= val import ja

CSS实现DIV三角形

本文内容收集来自网络 #triangle-up {width: 0;height: 0;border-left: 50px solid transparent;border-right: 50px solid transparent;border-bottom: 100px solid red;} #triangle-down {width: 0;height: 0;bor

创建一个大的DIV,里面的包含两个DIV是可以自由移动

创建一个大的DIV,里面的包含两个DIV是可以自由移动 <body>         <div style="position: relative; background:#DDF8CF;line-height: 50px"> <div style="text-align: center; width: 100%;padding-top: 0px;"><h3>定&nbsp;位&nbsp;

Codeforces Round 971 (Div. 4) (A~G1)

A、B题太简单,不做解释 C 对于 x y 两个方向,每一个方向至少需要 x / k 向上取整的步数,取最大值。 由于 x 方向先移动,假如 x 方向需要的步数多于 y 方向的步数,那么最后 y 方向的那一步就不需要了,答案减 1 代码 #include <iostream>#include <algorithm>#include <vector>#include <string>

CF#271 (Div. 2) D.(dp)

D. Flowers time limit per test 1.5 seconds memory limit per test 256 megabytes input standard input output standard output 题目链接: http://codeforces.com/contest/474/problem/D We s

CF #278 (Div. 2) B.(暴力枚举+推导公式+数学构造)

B. Candy Boxes time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output 题目链接: http://codeforces.com/contest/488/problem/B There