让我缓缓……写了整整三天,终于A掉了……这题,真的恶心啊……
这题最大的问题就是细节的问题,在码代码的时候记得一定要保持自己的状态,不能有一点的松懈,否则就像我一样……
WA了10+发,TLE了三四发,心累啊……
这题还是整体二分,对于那些写树套树的大佬我只能表示%%%了。
因为题目给出的条件是abs(c)<=n,那么可能存在负数,我们可以用n-c来替换c,那么c的值域就变成了[0,2*n],问题就变成了求区间内第k小的数了。
我们把修改和询问一起整体二分,取操作区间的一半,对于左半边的修改操作,我们把他们都加到树状数组中,并分到左边;否则就分到右边。
然后对于接下来的询问,若当前区间内数字个数超过询问需求量,那么就把这个询问分到左边,否则就修改询问需求量,把它分到右边。
接着清零树状数组,递归求解即可。
彩蛋:
1.再一次orzZH大佬,当我对着我那鬼畜WA掉的代码冥思苦想时,ZH大佬从旁边淡淡的来了一句:“还没AC?看看我的代码吧。”
QAQ……大佬不愧是大佬啊,太厉害啦,%%%%%%
2.原来优快云一天最多交五篇博客,我说为什么我交了五篇以后就一直显示“保存失败,请稍后重试”……
代码写的较丑,不喜勿喷QWQ
附上AC代码:
#include <cstdio>
#include <cctype>
#define N 60010
using namespace std;
struct note{
long long o,x,y,w,wz;
}a[N],q1[N],q2[N];
long long n,m,size,ans[N],t1[N],t2[N];
inline char nc(){
static char ch[100010],*p1=ch,*p2=ch;
return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}
inline void read(long long &a){
static char c=nc();int f=1;
for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
for (a=0;isdigit(c);a=a*10+c-'0',c=nc());
a*=f;return;
}
inline void updata(int x,long long w){
long long ww=(long long)w*x;
for (int i=x; i<=n; i+=i&-i) t1[i]+=w,t2[i]+=ww;
return;
}
inline long long query(int x){
long long sum=0;
for (int i=x; i; i-=i&-i) sum+=t1[i];
sum*=(long long)x+1;
for (int i=x; i; i-=i&-i) sum-=t2[i];
return sum;
}
inline void so(int l,int r,int ql,int qr){
if (l>r||ql>qr) return;
if (ql==qr){
for (int i=l; i<=r; ++i)
if (a[i].o==2) ans[a[i].wz]=ql;
return;
}
int mid=(ql+qr)>>1,ll=0,rr=0;
for (int i=l; i<=r; ++i)
if (a[i].o==1){
if (a[i].w<=mid) q1[++ll]=a[i],updata(a[i].x,1),updata(a[i].y+1,-1);
else q2[++rr]=a[i];
}
else{
long long tmp=query(a[i].y)-query(a[i].x-1);
if (a[i].w<=tmp) q1[++ll]=a[i];
else a[i].w-=tmp,q2[++rr]=a[i];
}
for (int i=1; i<=ll; ++i) if (q1[i].o==1) updata(q1[i].x,-1),updata(q1[i].y+1,1);
for (int i=1; i<=ll; ++i) a[l+i-1]=q1[i];
for (int i=1; i<=rr; ++i) a[l+ll+i-1]=q2[i];
return so(l,l+ll-1,ql,mid),so(l+ll,r,mid+1,qr);
}
int main(void){
read(n),read(m);
for (int i=1; i<=m; ++i){
read(a[i].o),read(a[i].x),read(a[i].y),read(a[i].w);
if (a[i].o==1) a[i].w=n-a[i].w;
else a[i].wz=++size;
}
so(1,m,0,n<<1);
for (int i=1; i<=size; ++i) printf("%lld\n",n-ans[i]);
return 0;
}
10.1更新
最近学了一波线段树套线段树(因为还是看不懂线段树套平衡树),回想起当时做这题的时候网上的题解都是树套树,就决定回来填一下坑。
刷题心得:
- 做题思路要清晰,不要在自己很累的时候做题。(特别是睡觉以前)
突然发现自己成为了以前自己仰望的那些大佬中的一个,终于初窥线段树套线段树的门径了(详情见上文)
对于修改操作,相当于单点修改权值线段树,区间修改区间线段树。即在每一个包含当前权值w的区间[l,r]对应的内层线段树上在给出的[L,R]上加一。
对于询问操作,相当于单点询问权值线段树,区间询问区间线段树。即对于当前区间[l,r]的右儿子询问区间和t,若t小于当前的排名,则往左儿子递归;否则更新排名,然后往右儿子递归。
因为这题的n<=50000,所以我们要动态开点。
树套树对于这题来说就是一个暴力的做法,不管是时间还是空间都被上面的整体二分艹爆了。
(实测——整体二分:7964kb,1784ms;树套树:470404kb,8780ms)(差距真的好大啊……)
p.s.不知为何,百度上排在搜索第一页的各位大佬的代码在洛谷和BZOJ上都WA掉了……只有CODE[VS]比较友好,都AC了……
附上AC代码:
#include <cstdio>
#define lt (k<<1)
#define rt (k<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=50005;
typedef long long ll;
ll sum[N*400],lz[N*400],w;
int n,m,x,y,o,size,ch[N*400][2],root[N<<2];
inline void change(int &k,int l,int r,int ql,int qr){
if (!k) k=++size;
sum[k]+=(ll)qr-ql+1;
if (l>=ql&&r<=qr) return (void)(++lz[k]);
if (qr<=mid) change(ch[k][0],l,mid,ql,qr);
else if (ql>mid) change(ch[k][1],mid+1,r,ql,qr);
else change(ch[k][0],l,mid,ql,mid),change(ch[k][1],mid+1,r,mid+1,qr);
}
inline void updata(int k,int l,int r,int ql,int qr,int w){
change(root[k],1,n,ql,qr);
if (l==r) return;
if (w<=mid) updata(lt,l,mid,ql,qr,w);
else updata(rt,mid+1,r,ql,qr,w);
}
inline ll ask(int k,int l,int r,int ql,int qr){
if (!k) return 0;
if (l>=ql&&r<=qr) return sum[k];
ll ret=1ll*lz[k]*(qr-ql+1);
if (qr<=mid) ret+=ask(ch[k][0],l,mid,ql,qr);
else if (ql>mid) ret+=ask(ch[k][1],mid+1,r,ql,qr);
else ret+=ask(ch[k][0],l,mid,ql,mid)+ask(ch[k][1],mid+1,r,mid+1,qr);
return ret;
}
inline int query(int k,int l,int r,int ql,int qr,ll w){
if (l==r) return l;
ll t=ask(root[rt],1,n,ql,qr);
if (t>=w) return query(rt,mid+1,r,ql,qr,w);
else return query(lt,l,mid,ql,qr,w-t);
}
int main(void){
scanf("%d%d",&n,&m);
while (m--){
scanf("%d%d%d%lld",&o,&x,&y,&w);
if (o==1) updata(1,1,n,x,y,w);
else printf("%d\n",query(1,1,n,x,y,w));
}
return 0;
}