先上題目: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 }