51nod1962区间计数

  考虑每个值作为最大值的区间实际上可以用单调栈求出来,即找到左边第一个比它大的数l[i],右边第一个比它大的r[i],那就是左端点在[l[i],i]右端点在[i,r[i]]的区间是以第i个数作为最大值。

  这样的话可以看成二维平面上一个矩形区域,每个矩形区域有着一样的最大值,如果我们把最大值相同的矩形放在一起考虑,问题就变成了:最大值相同的a产生的矩形和b产生的矩形交的面积之和。

  用一个扫描线去做,维护当前线上矩形交的长度now。具体是这样的:开2n个线段树,对于每一种最大值开线段树,分别用roota[i]和rootb[i]维护a和b的最大值为i的矩形在当前扫描线上覆盖情况。每次一个值为w的a产生的左上角[x1,y1],右下角[x2,y2]的矩形在x1处加入,now+=rootb[w]在[y1,y2]覆盖了多少,再在roota[w]的线段树修改。删除把+改成-就行了。这样最后ans+=now。

  写着非常简单。

 1 program j01;
 2 const maxn=350086;
 3 var a,b:array[0..maxn]of longint;
 4     roota,rootb:array[0..maxn]of longint;
 5     la,lb:array[0..maxn]of longint;
 6     sta,stb:array[0..maxn]of longint;
 7     f:array[0..40*maxn]of record l,r,tag,w:longint; end;
 8     topa,topb,cnt,n,i:longint;
 9     ll,rr,dd:longint;
10     sum,now:int64;
11
12 function min(a,b:longint):longint;inline;begin if a<b then exit(a) else exit(b); end;
13 function max(a,b:longint):longint;inline;begin if a>b then exit(a) else exit(b); end;
14
15 function ask(i,l,r:longint):longint;
16 var mid,res:longint;
17 begin
18     if i=0 then exit(0);
19     if(ll<=l)and(r<=rr)then exit(f[i].w);
20     mid:=(l+r)div 2;res:=f[i].tag*(min(rr,r)-max(ll,l)+1);
21     if ll<=mid then res:=res+ask(f[i].l,l,mid);
22     if mid+1<=rr then res:=res+ask(f[i].r,mid+1,r);
23     exit(res);
24 end;
25
26 procedure change(var i:longint;l,r:longint);
27 var mid:longint;
28 begin
29     if i=0 then
30     begin
31         inc(cnt);i:=cnt;
32     end;
33     if(ll<=l)and(r<=rr)then
34     begin
35         inc(f[i].tag,dd);inc(f[i].w,dd*(r-l+1));exit;
36     end;
37     mid:=(l+r)div 2;
38     if ll<=mid then change(f[i].l,l,mid);
39     if mid+1<=rr then change(f[i].r,mid+1,r);
40     f[i].w:=f[i].tag*(r-l+1)+f[f[i].l].w+f[f[i].r].w;
41 end;
42
43 begin
44     readln(n);
45     for i:=1 to n do read(a[i]);
46     for i:=1 to n do read(b[i]);
47     fillchar(la,sizeof(la),0);fillchar(lb,sizeof(lb),0);
48     fillchar(roota,sizeof(roota),0);fillchar(rootb,sizeof(rootb),0);
49     a[0]:=maxlongint;b[0]:=maxlongint;topa:=0;topb:=0;sta[0]:=0;stb[0]:=0;
50     sum:=0;now:=0;
51     for i:=1 to n do
52     begin
53         while a[sta[topa]]<=a[i] do
54         begin
55             ll:=la[sta[topa]];rr:=sta[topa];dd:=-1;
56             now:=now-ask(rootb[a[sta[topa]]],1,n);
57             change(roota[a[sta[topa]]],1,n);
58             dec(topa);
59         end;
60         while b[stb[topb]]<=b[i] do
61         begin
62             ll:=lb[stb[topb]];rr:=stb[topb];dd:=-1;
63             now:=now-ask(roota[b[stb[topb]]],1,n);
64             change(rootb[b[stb[topb]]],1,n);
65             dec(topb);
66         end;
67         la[i]:=sta[topa]+1;ll:=la[i];rr:=i;dd:=1;
68         now:=now+ask(rootb[a[i]],1,n);change(roota[a[i]],1,n);
69         inc(topa);sta[topa]:=i;
70         lb[i]:=stb[topb]+1;ll:=lb[i];rr:=i;dd:=1;
71         now:=now+ask(roota[b[i]],1,n);change(rootb[b[i]],1,n);
72         inc(topb);stb[topb]:=i;
73         sum:=sum+now;//writeln(now,‘ ‘,sum);
74     end;
75     writeln(sum);
76 end.
77
78         

时间: 2024-10-08 19:31:45

51nod1962区间计数的相关文章

51Nod1962 区间计数

这题与之前那道区间最值的题非常类似,依旧是二分区间,然后统计跨过中间点的区间贡献. 我们要选出小于等于和小于的,这样就可以算出相等的区间长了. 复杂度O(nlogn) By:大奕哥 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 ll ans;int n; 5 const int N=350005; 6 void add(ll x){ans+=x;} 7 int a[2][N],p[2][3]

POJ 3286- How many 0&#39;s?(组合数学_区间计数)

How many 0's? Time Limit:1000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 3286 Appoint description:  System Crawler  (2015-04-18) Description A Benedict monk No.16 writes down the decimal representations of

51NOD 1962 区间计数 单调栈+二分 / 线段树+扫描线

 区间计数 基准时间限制:1.5 秒 空间限制:262144 KB 分值: 80 两个数列 {An} , {Bn} ,请求出Ans, Ans定义如下: Ans:=Σni=1Σnj=i[max{Ai,Ai+1,...,Aj}=max{Bi,Bi+1,...,Bj}] 注:[ ]内表达式为真,则为1,否则为0. 1≤N≤3.5×1051≤Ai,Bi≤N 样例解释: 7个区间分别为:(1,4),(1,5),(2,4),(2,5),(3,3),(3,5),(4,5) Input 第一行一个整数N 第二行

POJ 2155 Matrix (树状数组 &amp;&amp; 区间计数)

题意 : 给出一个N*N的矩阵, 矩阵只有可能包含0或1, 一开始则全部是0.对于矩阵可以进行两种操作, 第一种是输入 C x1 y1 x2 y2 表示, 对以(x1, y1)为左上角, 以(x2, y2)为右下角构成的矩形区域内的数全部进行取反操作, 即0变1.1变0.第二种是Q X Y, 表示查询现在这个矩阵的(X, Y)点到底是0还是1.总共有T次操作, 对于C操作进行相应的修改, 对于Q操作要对应输出! 分析 : 据说是楼教主出的题, 自己确实想不出什么高效的办法, 参考网上的题解, 才

POJ 2282-The Counting Problem(组合数学_区间计数)

The Counting Problem Time Limit:3000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2282 Appoint description:  System Crawler  (2015-04-15) Description Given two integers a and b, we write the numbers between

51nod 1962 区间计数(单调栈+二分)

维护两个单调递减的栈,当i加进栈,位置x的数弹出的时候,在另一个栈中找到和这个数一样大的数,计算贡献(x-靠右左端点)*(i-x). #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=500010,in

区间DP总结

做了一些区间DP的题目,总结如下 1.Multiplication Puzzle 原题地址:http://poj.org/problem?id=1651 题意: 给定一个序列,可以依次从序列中取走除了左右两端点之外的元素,每次取走一个元素,获得该元素乘以它左右两边元素乘积的点数,求可能的最小点数 题解: 枚举区间中最后一个被取走的元素,实现区间的分割,也就是状态的转移. 详细的解题报告 2.Dire Wolf 原题地址:http://acm.split.hdu.edu.cn/showproble

[Gym-101981J] Prime Game (组合计数)

题意:求for(int i=1;i<=n;i++) for(int j=i;j<=n;j++) sum += f[i][j]; f[i][j]表示在序列从 i 位乘到第 j 位所形成的新的数的 不同质因子的个数. 思路:说是话,拿到题还是一开始想着能不能进行递推,比如先将每一个数进行 质因分解 然后用set不断更新统计个数来求和.但这样无论怎样都无法优化 (n^2) ,所以换思路再想. 就忽然想到了以前有一道做过的原题,题意是:给定一个长度为n的序列,然后求出每一个子区间不同数的个数和.而这一

topK

最大K个数: 当数据量小时:快排和堆排O(Nlog(N)):部分排序(选择or交换)O(N*K) 快排加分治O(N*log(K)):二分查找 当数据是整数且重复数比较多时:计数排序:若不是整数,则分区间计数. 当数据量大时: 1)小根堆:O(N*KlogK) 2)分治法:hash成M份数据,取每份数据的前K个.然后使用快排(分治)对M*K个数据求出K个数.[用MapReduce] 实际应用:当数据量大,且重复数据多时,求频率最高的K个 1)先用hash_map进行频率统计(得到一个数组) 2)然