CF1181D Irrigation(权值线段树)
这道题目考察了对于数据结构模型的运用和对于题目信息的理解与反思
首先观察到的是k很大,因此普通的方法并不可行。
但是观察题目可以发现,当所有城市的数量都是相同的时候,一定是按照一个循环取模就能找到答案
现在的问题是如何度过到将所有城市都相等。
题目的要求是,先选个数小的,第二关键字是标号小的,想象一下,如果将已知条件情况下的城市排序,那么一定是最小个数相同的城市,他们内部是通过一定顺序来增长,整体是一起增长
直到达到最小值。因此我们可以考虑分两种情况来讨论,一种是,当前询问的天数<=当前最小值集合,这样的话其实就是在最小值的集合中取k大值模型,因为我们要求按标号从小到大。
反之,最小值集合集体+1,我们下一步再去计算他。
具体做法是:对于前n个输入,我们将保留每一天有哪些城市。之后枚举天数,就能不断的增加最小值集合。查询的方式就是利用权值线段树进行查找。当天数枚举完后。
一定存在一种情况是所有城市的天数相等,对于这种情况,只需要取模查询即可。
另外,这种做法必须要对询问排序,因为操作不可逆,只有按天数从小到大查询复杂度才是正确的并且能够得到答案
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll,ll> pll; typedef pair<int,pll> plll; const int N=5e5+10; const int inf=0x3f3f3f3f; const int mod=1e9+7; int a[N]; vector<int> num[N]; struct ask{ ll k; int id; bool operator <(const ask &t) const{ return k<t.k; } }s[N]; struct node{ int l,r; int cnt; }tr[N<<2]; ll ans[N]; void build(int u,int l,int r){ if(l==r){ tr[u]={l,r,0}; } else{ tr[u]={l,r,0}; int mid=l+r>>1; build(u<<1,l,mid); build(u<<1|1,mid+1,r); } } void modify(int u,int x,int k){ if(tr[u].l==tr[u].r){ tr[u].cnt+=k; return ; } int mid=tr[u].l+tr[u].r>>1; if(x<=mid) modify(u<<1,x,k); else modify(u<<1|1,x,k); tr[u].cnt=tr[u<<1].cnt+tr[u<<1|1].cnt; } int query(int u,ll k){ if(tr[u].l==tr[u].r) return tr[u].l; if(tr[u<<1].cnt>=k) return query(u<<1,k); else return query(u<<1|1,k-tr[u<<1].cnt); } int main(){ ios::sync_with_stdio(false); int n,m,q; cin>>n>>m>>q; int i; //计算每个城市的举行次数 for(i=1;i<=n;i++){ int x; cin>>x; a[x]++; } //计算每天有哪些举行城市 for(i=1;i<=m;i++){ num[a[i]].push_back(i); } for(i=1;i<=q;i++){ cin>>s[i].k; s[i].k-=n; s[i].id=i; } //离线询问,原因是操作是不可逆的,因此要从小往大 sort(s+1,s+1+q); ll sum=0,cur=0;//sum计算总共前面操作了多少次,cur计算当前有多少个需要操作 int it=1; build(1,1,m); for(i=0;i<=n;i++){ for(auto x:num[i]) modify(1,x,1),cur++; while(it<=q&&s[it].k-sum<=cur) ans[s[it].id]=query(1,s[it].k-sum),it++; sum+=cur; } while(it<=q){ ans[s[it].id]=query(1,(s[it].k-sum-1)%m+1); it++; } for(i=1;i<=q;i++){ cout<<ans[i]<<endl; } return 0; }
没有人不辛苦,只有人不喊疼