jzoj. 4298. 【NOIP2015模拟11.2晚】我的天

67 篇文章 1 订阅
26 篇文章 0 订阅

Description

很久很以前,有一个古老的村庄——xiba村,村子里生活着n+1个村民,但由于历届村长恐怖而且黑暗的魔法统治下,村民们各自过着独立的生活,完全没有意识到其他n个人的存在。
但有一天,村民xiba臻无意中也得到了魔法,并发现了这个恐怖的事实。为了反抗村长,他走遍了全世界,找到了其他n个村民,并组织他们发动革命。但让这n个素不相识的村民(xiba臻已跟他们认识)同心协力去抵抗村长是很困难的,所以xiba臻决定先让他们互相认识。
这里,xiba臻用了xiba村特有的xiba思维:先让这n个人排成一列,并依次从1-n标号。然后每次xiba臻会选出一个区间[l, r],在这个区间中的人会去认识其他在这个区间中的人,但已经认识过得不会再去认识。这样,进行m次操作后,xiba臻认为这n个人能认识到许多人。
但是,为了精确地知道当前有多少对人已经认识了,xiba臻想要知道每次操作后会新产生出多少对认识的人,但这已是xiba思维无法解决的事了,你能帮帮他吗?

Input

第一行两个整数n,m。
接下来m行每行两个整数li,ri,表示每次操作的区间。

Output

共m行,每行一个整数ans_i,表示第i次操作后新产生出ans_i对认识的人。

Sample Input

5 5
2 3
2 4
3 5
1 5
2 4

Sample Output

1
2
2
5
0

Data Constraint

对于20%的数据,1≤n,m≤100。
对于50%的数据,1≤n,m≤5000。
对于100%的数据,1≤n,m≤300000,1≤li≤ri≤n。

背景:做完这题,真的想说一句我的天。

分析:
20%,纯暴力。
50%,设f[i]为某人i能认识i–f[i]的人,显然这个数组具有单调性。对于每次询问l–r,枚举区间内每个人,因为当前某人i能使前i个人认识,现在有让他与l–i-1的人认识(为不重复,只讨论每个人前面的人),显然增加了f[i]-l个人,统计和,即可。
70%:由于具有单调性,如果f[i]<=l,即已经认识了前面在区间内的所有人,显然对答案无贡献,那后面的点也没有贡献啦。
100%:(正解):用线段树维护全间最大值和区间和,用50%方法即可。
(水解):这个方法不知在各大题库能否过。70%的点是从l–r枚举,变为从r–l枚举,因为数据的问题,倒着枚举会先break掉,不会超时,甚至会比正解快。

代码:

var
 i,j,l,r,n,m:longint;
 ans:int64;
 f:array [1..300001] of longint;

begin
 assign(input,'ohmygod.in');
 assign(output,'ohmygod.out');
 reset(input);
 rewrite(output);
 readln(n,m);
 for i:=1 to n do
  f[i]:=i;
 for i:=m downto 1 do
  begin
   readln(l,r);
   ans:=0;
   for j:=r downto l do
    begin
     if f[j]>l then
      begin
       ans:=ans+f[j]-l;
       f[j]:=l;
      end
     else break;
    end;
   writeln(ans);
  end;
 close(input);
 close(output);
end.
type
        cy=record
                sum:int64;
                lab,max:longint;
        end;
const
        maxn=300005;
var
        n,m,i,j,x,y,z:longint;
        a,ans:array[0..maxn]of int64;
        tree:array[0..maxn*4]of cy;
function
        max(x,y:longint):longint;
begin
        if x>y then exit(x)
        else exit(y);
end;
procedure
        maketree(p,l,r:longint);
var
        mid:longint;
begin
        if l=r then
        begin
                tree[p].sum:=a[l];
                tree[p].max:=a[l];
                tree[p].lab:=0;
                exit;
        end;
        mid:=(l+r)shr 1;
        maketree(p<<1,l,mid);
        maketree(p<<1+1,mid+1,r);
        tree[p].max:=max(tree[p<<1].max,tree[p<<1+1].max);
        tree[p].sum:=tree[p<<1].sum+tree[p<<1+1].sum;
end;
procedure
        push(x,p,l,r,mid:longint);
begin
        tree[p<<1].max:=x;
        tree[p<<1].sum:=x*(mid-l+1);
        tree[p<<1].lab:=x;
        tree[p<<1+1].max:=x;
        tree[p<<1+1].sum:=x*(r-mid);
        tree[p<<1+1].lab:=x;
        tree[p].lab:=0;
end;
function
        find(p,l,r,t:longint):longint;
var
        mid:longint;
begin
        if l=r then exit(l)
        else
        begin
                mid:=(l+r)>>1;
                if tree[p].lab<>0 then push(tree[p].lab,p,l,r,mid);
                if tree[p<<1].max>t then exit(find(p<<1,l,mid,t))
                else exit(find(p<<1+1,mid+1,r,t));
        end;
end;
procedure
        change(p,l,r,a,b,t:longint);
var
        mid:longint;
begin
        if (l=a)and(r=b) then
        begin
                tree[p].max:=t;
                tree[p].sum:=t*(b-a+1);
                if l<>r then tree[p].lab:=t;
                exit;
        end;
        mid:=(l+r)>>1;
        if tree[p].lab<>0 then push(tree[p].lab,p,l,r,mid);
        if b<=mid then change(p<<1,l,mid,a,b,t)
        else if a>mid then change(p<<1+1,mid+1,r,a,b,t)
        else
        begin
                change(p<<1,l,mid,a,mid,t);
                change(p<<1+1,mid+1,r,mid+1,b,t);
        end;
        tree[p].max:=max(tree[p<<1].max,tree[p<<1+1].max);
        tree[p].sum:=tree[p<<1].sum+tree[p<<1+1].sum;
end;
begin
        assign(input,'ohmygod.in'); reset(input);
        assign(output,'ohmygod.out'); rewrite(output);
        readln(n,m);
        for i:=1 to n do a[i]:=i;
        maketree(1,1,n);
        ans[0]:=tree[1].sum;
        for i:=1 to m do
        begin
                readln(x,y);
                if tree[1].max<=x then ans[i]:=ans[i-1]
                else
                begin
                        if x<>y then
                        begin
                                z:=find(1,1,n,x);
                                if z<=x then change(1,1,n,x,y,x)
                                else if z>y then ans[i]:=ans[i-1]
                                else change(1,1,n,z,y,x);
                                ans[i]:=tree[1].sum;
                        end
                        else ans[i]:=ans[i-1];
                end;
                writeln(ans[i-1]-ans[i]);
        end;
        close(input); close(output);
end.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值