Codeforces Round #716 (Div. 2) D. Cut and Stick (贪心,主席树)

-
题意:给你一个序列,有\(q\)个询问,每次询问一个区间\([l,r]\),你要将这个区间内的数最少分成几个子序列,使得每个序列中的众数出现的次数不大于\(\lceil \frac{x}{2}\rceil\)(\(x\)为子序列区间长度).
-
题解:假如区间内众数出现的次数\(cnt<=\lceil \frac{x}{2}\rceil\).我们就不需要分裂.否则,区间内不是众数的个数为\(x-cnt\),不难发现能构造出的最长区间一定是\(x-cnt+(x-cnt)+1\),即用这些不是众数的数去和众数放在一个子序列内, 这个序列构造完后,就只剩下众数了,他们只能单独一个数一个序列,那么答案就是\(x-(x-cnt+(x-cnt)+1)+1=2*cnt-x\).然后我们可以用主席树来求出区间内众数出现的次数,直接算答案即可.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,q; int a[N]; int root[N],idx; struct misaak{ int l,r; int cnt; }tr[4*N+N*20]; int build(int l,int r){ int p=++idx; if(l==r) return p; int mid=(l+r)>>1; build(l,mid),build(mid+1,r); return p; } int update(int p,int l,int r,int x){ int q=++idx; tr[q]=tr[p]; tr[q].cnt++; if(l==r) return q; int mid=(l+r)>>1; if(x<=mid) tr[q].l=update(tr[p].l,l,mid,x); else tr[q].r=update(tr[p].r,mid+1,r,x); return q; } int query(int L,int R,int l,int r,int k){ if(l==r){ //cout<<L<<' '<<R<<' '<<tr[L].cnt<<' '<<tr[R].cnt<<'\n'; return tr[R].cnt-tr[L].cnt; } int cnt1=tr[tr[R].l].cnt-tr[tr[L].l].cnt; int cnt2=tr[tr[R].r].cnt-tr[tr[L].r].cnt; int mid=(l+r)>>1; int res=0; if(k<=cnt1) res=max(res,query(tr[L].l,tr[R].l,l,mid,k)); else if(k<=cnt2) res=max(res,query(tr[L].r,tr[R].r,mid+1,r,k)); return res; } int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>q; rep(i,1,n) cin>>a[i]; root[0]=build(1,n); rep(i,1,n){ root[i]=update(root[i-1],1,n,a[i]); } while(q--){ int l,r; cin>>l>>r; int res=query(root[l-1],root[r],1,n,(r-l+1)/2+1); cout<<max(1,2*res-(r-l+1))<<'\n'; } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮

浙公网安备 33010602011771号