【学习笔记】CF1375

这题需要自己构造过程。但是我这方面能力比较差所以还是不会做

显然倒着考虑。如果先手胜利的话那么 a , b , c a,b,c a,b,c一定构成等差数列,并且上一次操作的是 c c c

再往前倒推一步。接下来就非常有运气成分了:假设 c c c不能操作,并且任意操作 a , b a,b a,b都会构成等差数列,解方程算出来 k = 2 c − a − b k=2c-a-b k=2cab恰好有解,这道题就做完了。

注意到是对相邻的点进行操作,因此猜测存在距离之类的不变量。

假设固定了根节点,那么问题转化为将所有点到根的距离变成 1 1 1,不难发现操作过程中只有一个点距离的奇偶性发生变化,猜测这就是答案的下界。取等分析就不说了。

向来不会工业题

首先考虑一个操作次数 N 2 N^2 N2的解法,我们需要求出所有区间对应的集合

M = ⌊ N 2 ⌋ M=\lfloor\frac{N}{2}\rfloor M=2N,首先求出数组 a i a_i ai权值在 [ 1 , M ] [1,M] [1,M]的子序列对应的所有集合,以及数组 a i a_i ai权值在 [ M + 1 , N ] [M+1,N] [M+1,N]的子序列对应的所有集合,那么原数组的任意区间都可以由至多一次操作拼出。不难验证操作次数不会超过 N 2 N^2 N2

因为这题很考验代码实现能力,所以还是梳理一下代码实现的细节,不然写起来非常想死

对于原问题,我们考虑对权值进行分块处理 一开始想的是按序列进行分块但是就是很难写所以写不出来,应该是想复杂了 ,对于每一个值域块,我们提取出 [ l : r ] [l:r] [l:r](询问区间)中在值域块内的数的子序列加入答案,那么对于一个值域块,我们要维护 [ l , r ] [l,r] [l,r]所对应的答案,转化到子序列中就是 [ l ′ , r ′ ] [l',r'] [l,r]对应的答案。于是只用考虑怎么合并。首先求出 [ l , M ] , [ M + 1 , r ] [l,M],[M+1,r] [l,M],[M+1,r]对应的答案(这可以用一个结构体来维护),然后枚举区间,把它在左右两段值域中所对应的两个区间合并起来就是答案。这个地方可以直接二分查找 因为这样比较好写 。取 B = Q B=\sqrt{Q} B=Q ,操作次数我们之前分析过了是 2 N Q 2N\sqrt{Q} 2NQ 次,恰好可以通过。

权衡利弊后,发现还是写一下吧,要是代码能力再退步那就寄了

为了避免繁琐的空间问题,我选择使用 vector \text{vector} vector

这题也引发了我的思考。遇到这样代码比较复杂的题目,能不能在考场上迅速想到最好实现的那种方法呢?因为我们知道数据结构题实现思路不清晰的话甚至不如暴力。那么我们要冷静下来分析,必要时可以牺牲少量常数换取代码长度,这是对我这样代码能力“不那么强”的oier所需要的。

#include<bits/stdc++.h> #define pb push_back #define fi first #define se second #define inf 0x3f3f3f3f using namespace std; const int N=(1<<12)+5; const int M=(1<<16)+5; const int B=1<<8; int n,m,Q,a[N],b[N],id[M]; vector<pair<int,int>>ans; struct node{ vector<vector<int>>v; vector<int>p;//原序列中的位置 void init(int l,int r){ int len=r-l+1; v.resize(len); for(int i=0;i<len;i++)v[i].resize(len); for(int i=l;i<=r;i++)p.pb(b[i]); sort(p.begin(),p.end()); } int ask(int l,int r){ int ql=lower_bound(p.begin(),p.end(),l)-p.begin(),qr=upper_bound(p.begin(),p.end(),r)-p.begin()-1; if(ql<=qr)return v[ql][qr]; return 0; } }res[N]; int merge(int x,int y){ if(!x||!y)return x+y; ans.pb({x,y}); return ++m; } node getres(int l,int r){ node ans; ans.init(l,r); if(l==r){ ans.v[0][0]=b[l]; return ans; } int mid=l+r>>1; node L=getres(l,mid),R=getres(mid+1,r); int n=r-l+1; for(int i=0;i<n;i++){ for(int j=i;j<n;j++){ ans.v[i][j]=merge(L.ask(ans.p[i],ans.p[j]),R.ask(ans.p[i],ans.p[j])); } } return ans; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>Q;m=n; for(int i=1;i<=n;i++)cin>>a[i],b[a[i]]=i; for(int i=1;i<=(n-1)/B+1;i++){ int l=(i-1)*B+1,r=min(n,i*B); res[i]=getres(l,r); } for(int i=1;i<=Q;i++){ int l,r,cur=0; cin>>l>>r; for(int j=1;j<=(n-1)/B+1;j++){ cur=merge(cur,res[j].ask(l,r)); } id[i]=cur; } cout<<ans.size()+n<<"\n"; for(auto x:ans)cout<<x.fi<<" "<<x.se<<"\n"; for(int i=1;i<=Q;i++)cout<<id[i]<<" "; }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530014.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(4)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示