[DS记录]P4587 [FJOI2016]神秘数

command_block

2021-06-28 10:51:26

Personal

**题意** : 对于可重整数集 $S$ ,定义 ${\rm nsum}(S)$ 为最小的不能被 $S$ 的一个子集的和表示出来的正整数。 给定序列 $A_{1\sim n}$ ,每次询问 ${\rm nsum}\big(\{A_{l\sim r}\}\big)$。 $n,m\leq 10^5,\sum A_{1\sim n}\leq 10^9$ ,时限$\texttt{1s}$。 ------------ 考虑如何回答单组询问。 将 $S$ 从小到大排序为 $S_{1\sim m}$。 不难发现,找到第一个 $S_i>\sum S_{1\sim i-1}+1$ ,则 ${\rm nsum}(S)=\sum S_{1\sim i-1}+1$。 回到原问题。 用主席树差分得到区间内的值域线段树。 设当前的候选答案为 $ans$ (初始时为 $1$),求 $\leq ans$ 的数的和 $S$ ,若 $ans\geq S+1$ ,则答案为 $S+1$。 若 $S+1>ans$ ,则令 $ans=S+1$ ,继续操作。 记连续某两次的 $ans$ 为 $x,y$ ,若不停止,则 $y$ 的查询中 $(x,y]$ 必然要有数,下一步至少变为 $x+y+1$。故操作次数是 $O(\log)$ 的。 复杂度为 $O\big(n\log n\log(\sum A)\big)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 100500 using namespace std; const int lim=1000000000; struct Node{int l,r,s;}a[MaxN<<5]; int tn,rt[MaxN],to,wfr,ret; void add(int l,int r,int &u,int v) { a[u=++tn]=a[v];a[u].s+=to; if (l==r)return ; int mid=(l+r)>>1; if (to<=mid)add(l,mid,a[u].l,a[v].l); else add(mid+1,r,a[u].r,a[v].r); } void qry(int l,int r,int u) { if (r<=wfr){ret+=a[u].s;return ;} int mid=(l+r)>>1; qry(l,mid,a[u].l); if (mid<wfr)qry(mid+1,r,a[u].r); } ll n;int m; int main() { scanf("%d",&n); for (int i=1,x;i<=n;i++){ scanf("%d",&x); to=x;add(1,lim,rt[i],rt[i-1]); } scanf("%d",&m); for (int i=1,l,r;i<=m;i++){ scanf("%d%d",&l,&r); int ans=1; while(1){ ret=0;wfr=ans; qry(1,lim,rt[l-1]);ret=-ret; qry(1,lim,rt[r]); if (ans>=ret+1)break; else ans=ret+1; }printf("%d\n",ret+1); }return 0; } ``` ## 加强 :[U164888 五重原题](https://www.luogu.com.cn/problem/U164888?contestId=45705) 在上一题的基础上增加单点修改操作。 $n\leq 5\times 10^5,q\leq 10^5$ ,时限$\texttt{2s}$。 ------------ 可以用树套树来维护“区间内 $\leq x$ 的数的和”,复杂度高达 $\log^3$ ,无法通过。 将值域按照 $[2^i,2^{i+1})$ 分块。每次操作,(由于开始时是 $2^i$)要么跳过一整块,要么停止在中间。 于是对于每一块,维护 $\min,sum$ ,判一下 $\min$ 会不会停止即可。 时间复杂度为 $O(n\log n\log a)$ ,空间复杂度为 $O(n\log a)$。 底层按 $\log a$ 分块可以将空间复杂度改进为 $O(n)$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 530000 using namespace std; const int BS=32,INF=2000000000; struct Node{ ll s[31];int x[31]; void clear() {for (int i=0;i<30;i++){s[i]=0;x[i]=INF;}} }a[(MaxN/BS)<<1],R; Node operator + (const Node &A,const Node &B){ Node C; for (int i=0;i<30;i++)C.s[i]=A.s[i]+B.s[i]; for (int i=0;i<30;i++)C.x[i]=min(A.x[i],B.x[i]); return C; } inline int log2(int x){ int cnt=0; while((x>>5)>=1){x>>=5;cnt+=5;} while(x>1){x>>=1;cnt++;} return cnt; } int x[MaxN]; void _build(int l,int r,Node &A) { A.clear(); for (int i=l;i<=r;i++){ int p=log2(x[i]); A.x[p]=min(A.x[p],x[i]); A.s[p]+=x[i]; } } inline void up(int u){a[u]=a[u<<1]+a[u<<1|1];} void build(int l,int r,int u) { if (r-l+1<=BS){_build(l,r,a[u]);return ;} int mid=(l+r)>>1; build(l,mid,u<<1); build(mid+1,r,u<<1|1); up(u); } int to; void upd(int l,int r,int u) { if (r-l+1<=BS){_build(l,r,a[u]);return ;} int mid=(l+r)>>1; if (to<=mid)upd(l,mid,u<<1); else upd(mid+1,r,u<<1|1); up(u); } int wfl,wfr; void qry(int l,int r,int u) { if (r-l+1<=BS){ Node now; _build(max(wfl,l),min(wfr,r),now); R=R+now;return ; } if (wfl<=l&&r<=wfr){R=R+a[u];return ;} int mid=(l+r)>>1; if (wfl<=mid)qry(l,mid,u<<1); if (mid<wfr)qry(mid+1,r,u<<1|1); } int n,m; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++)scanf("%d",&x[i]); build(1,n,1); for (int i=1,op,l,r;i<=m;i++){ scanf("%d%d%d",&op,&l,&r); if (op==1){x[to=l]=r;upd(1,n,1);} else { R.clear();wfl=l;wfr=r;qry(1,n,1); ll sum=0; for (int i=0;i<30;i++) if (sum+1>=R.x[i]) sum+=R.s[i]; printf("%lld\n",sum+1); } }return 0; } ```