题目链接:http://202.121.199.212/JudgeOnline/problem.php?id=1716
【题意】
1到N的区间,一种操作让编号从a到b的数变为z,但不会低于2,问多次操作后大于2的数减2后的和为多少。
【分析】
本来这题可以用线段树模拟过的,但是这里的N非常大,达到109,开个一维数组就会爆内存,更何况开个线段树。
分析题目后不难发现最后的一个操作一定生效,之前的操作如果有涉及之后操作区间的部分就会失效。根据这条性质,从操作的后面往前扫描,更新区间内的元素,如果元素被更新过就忽略,这样根据所有元素更新后的值就能算出结果了。如果用元素标记的方法一来时间复杂了,二来内存不够,所以绝对不能开一个一位数组来标记,也就是说这种方法不可行。
于是可以想到,把操作看成区间线段,从后往前坐做,每次只要更新之前未被区间覆盖到的元素,现在的问题是,区间可能交叉重叠,如何快速地判断区间是否已被覆盖?如果区间被之前的区间分为多个部分,如何区分?
以下为错误做法,但是AC了,数据太弱~
使用STL的MAP,map<int,int>表示 key之前的区间的元素值为value 。
对于每次的更新区间,处理其端点,二分查找与两个端点最近的且在右边的key值和相对应的value,来判断是否被覆盖,并更新。
假设依次处理红蓝绿三个区间,先查找与a、c相近的key,都没找到,就认为不存在覆盖,访问下map[a],但值不动(如过之前没访问过就获得新值0,如果已经访问过,则保留原值,如果改变其值对端点重叠的情况会算错);map[c]=value1。处理蓝色时b找到c,map[c]的值存在,所以b到c的区间是被覆盖的,不处理,map[e]=value2。 同理[d,e]被覆盖,map[f]=value3。这样就被分出3个有效区间[a,c]=value1、[c+1,e]=value2、[e+1,f]=value3。还有些细节处理下,这个算法就能AC题目了。
代码:
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <map> 5 #include <algorithm> 6 using namespace std; 7 map<int,long long> su; 8 int n,m; 9 struct node 10 { 11 int a,b,z; 12 void read() 13 { 14 scanf("%d%d%d",&a,&b,&z); 15 } 16 }data[200003]; 17 int main () 18 { 19 while (~scanf("%d",&n)) 20 { 21 su.clear(); 22 scanf("%d",&m); 23 for (int i=0;i<m;++i) 24 data[i].read(); 25 for (int i=m-1;i>=0;--i) 26 { 27 map<int,long long>::iterator p1=su.upper_bound(data[i].a); 28 map<int,long long>::iterator p2=su.upper_bound(data[i].b); 29 int pp1,pp2; 30 long long v1,v2; 31 if (p1==su.end()) pp1=-1; else {pp1=p1->first;v1=su[pp1];} 32 if (p2==su.end()) pp2=-1; else {pp2=p2->first;v2=su[pp2];} 33 34 if (pp1==-1 || v1<=2) 35 { 36 //cout<<su[data[i].a]<<endl; 37 su[data[i].a]; 38 if (pp1!=-1 && data[i].b+1>pp1)su[pp1]=data[i].z; //这里特殊区间要判断排除下 39 } 40 if (pp2==-1 || v2<=2) 41 { 42 su[data[i].b+1]=data[i].z; 43 } 44 45 } 46 long long sum=0; 47 map<int,long long>::iterator i=su.begin(); 48 //cout<<i->first<<" "<<i->second<<endl; 49 int pp=i->first; 50 ++i; 51 for (;i!=su.end();++i) 52 { 53 if (i->second>=2) 54 { 55 // cout<<i->first<<" "<<i->second<<endl; 56 int tem=i->first; 57 if (tem>n+1) tem=n+1; 58 sum+=(long long )(i->second-2)*(long long)(tem-pp); 59 } 60 pp=i->first; 61 } 62 cout<<sum<<endl; 63 } 64 }