先上题目:ios
时间限制:1000MS 内存限制:65535K
提交次数:288 经过次数:22编程
题型: 编程题 语言: C++;C数组
Acm比赛里面常常须要巧妙方法处理数据,如求给定数组的最大连续和就可用到前缀和。如今给出一个给定长度的且初始化为0的数组, 而后对该数组的一系列区间作取反操做,最后询问数组中有多少个1。(取反操做:原来是0就变为1,是1就变为0)
输入第一行是t表示case数。每一个case的第一行是用空格隔开的正整数n和m(0<n<=10^18,0<=m<=100000)分别表示数组长度和总操做数, 接下来有m行,每行包含两个整数a和b(a、b之间用空格隔开,表示将位置a和位置b之间(包含a和b)的全部位进行取反操做,0<=a,b<n)。每一个case后紧接一空行。
每一个case第一行输出:“Case #i:”,i表示第几个Case(从1开始);第二行输出数组1的个数
2 2000000000 1 0 1000000000 1000 3 0 999 0 100 100 100
Case #1: 1000000001 Case #2: 900
首先是最简单的方法:读入全部的区间,而后将区间里面的全部值取反。这样作绝对超时。ide
而后是深一层的方法:先对全部区间离散化,而后构造线段树,接着更新线段树的区间。思路上可行,没有用这种方法实现过,算了一下时间复杂度:spa
①排序+离散化 O(mlogm)code
②建树 O(mlogm)blog
③更新区间 O(mlogm)排序
④最终求值O(mlogm)ip
这是上界,感受不会超,可是实现起来仍是比较麻烦。内存
而后说一下第三种方法:使用归一化思想,见过观察,咱们能够发现更新一个区间[l,r]的操做等价于更新[l,+∞]而后再更新[r+1,+∞],因此咱们能够先处理出全部的变换后的左区间,而后排一次序,从小到大排序,而后加减交替更新便可,由于更新奇数次为加,更新偶数次不变。总的时间复杂度为O(mlogm)
上代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <utility> 5 #include <vector> 6 #include <algorithm> 7 #define MAX 100002 8 #define ll long long 9 //#define lld I64d 10 using namespace std; 11 12 ll n,m; 13 ll l,r; 14 ll s[MAX<<1],tot; 15 16 int main() 17 { 18 int t; 19 //freopen("data.txt","r",stdin); 20 //ios::sync_with_stdio(false); 21 scanf("%d",&t); 22 for(int z=1;z<=t;z++){ 23 tot=0; 24 scanf("%lld %lld",&m,&n); 25 for(int i=0;i<n;i++){ 26 scanf("%lld %lld",&l,&r); 27 if(l>r) swap(l,r); 28 s[tot++]=l; 29 s[tot++]=r+1; 30 } 31 sort(s,s+tot); 32 ll sum=0,f=1; 33 for(int i=0;i<tot;i++){ 34 if(s[i]>m) break; 35 if(f){ 36 sum+=m-s[i]+1; 37 }else{ 38 sum-=m-s[i]+1; 39 } 40 f=f^1; 41 } 42 //cout<<"Case #"<<z<<":"<<endl<<sum<<endl; 43 printf("Case #%d:\n%lld\n",z,sum); 44 } 45 return 0; 46 }