多校聯合訓練2016---hdu5798Stabliztion
一個壓縮狀態 + 記憶搜尋/DFS非順序列舉 的題目。。
TLE的同學記得一定要用G++提交。否者如果是C ++ ,標程都TLE。表示時間限制很緊,標程都是1800ms,我的是2800ms......
首先很容易想到的思路是,,X的每一位如果是1.都會對10^5個數產生影響。。記錄變化下來,然後列舉X,再迴圈x的每一位,如果是1,就加上這個變化。。
但是也就自然的把10^5-1個差壓縮成了d[20],也就是i位變化,所有的數的變化和。。。
但是考慮到,如果兩個相鄰的數。他們的最高的不同位發生了變化,那麼兩個的相對大小會發生變化。。那麼之前求的j位(j < i)變化,要取反。。。
這樣就加加入了一維。。d[i][j]表示最高不同位是i的所有數。。j位發生變化產生的總的影響是多少。。其實也就相當於是求貢獻。。每一個數的貢獻和就是結果,但是每一個數都有些許不同(最高不同位)這樣就要分開來。所以引入了那個陣列。這個思想是很重要的,也是找出狀態的關鍵。。
有了d陣列,這樣的話。樸素列舉每一個x,每一個x中在對d的兩維列舉,滿足條件就+d[i][j],但是注意取反的情況,這樣也就很簡單了。
可是20*20*2^20會超時。。。
考慮優化:找出不同的x中,有沒有共同點。。因為
0000100010101
1010100010101
對於上面兩個資料,考慮第一個數第一個1時,如果算出來了,那麼對於下面的第3個1,因為後面完全相同,雖然前面不同,但是因為最高位是在後面。,這些不同的位不產生影響。所以,還是相等的。。。
這樣考慮記錄陣列t[20][2^20]。。。記錄資料。,。
然後發現會超空間。。。
但是用貢獻的思路考慮。先列舉x,在列舉i,和先列舉i然後在列舉x,對於每一個x貢獻是一樣的。。
所以可以改變列舉順序。。。。(很重要的思路) ;
就可以把t陣列變為t[2^20] ; 很叼的題目。。很好。。。
#include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <map> #include <set> #include <stack> #include <queue> #include <cmath> #include <string> #include <vector> #include <cstdio> #include <cctype> #include <cstring> #include <sstream> #include <cstdlib> #include <iostream> #include <algorithm> #pragma comment(linker,"/STACK:102400000,102400000") using namespace std; #define MAX 1000005 #define MAXN 2000005 #define maxnode 500010 #define sigma_size 30 #define lson l,m,rt<<1 #define rson m+1,r,rt<<1|1 #define lrt rt<<1 #define rrt rt<<1|1 #define mid int m=(r+l)>>1 #define LL long long #define ull unsigned long long #define mem0(x) memset(x,0,sizeof(x)) #define mem1(x) memset(x,-1,sizeof(x)) #define meminf(x) memset(x,INF,sizeof(x)) #define lowbit(x) (x&-x) #define S(n) scanf("%d",&n) #define P(n) printf("%d ",n) #define PN(n) printf("%d\n",n) #define FP(k) freopen(k , "r" ,stdin) #define RPTI(s , n) for(int i=s;i<n;i++) #define RPTJ(s , n) for(int j=s;j<n;j++) #define RPTK(s , n) for(int k=s;k<n;k++) #define RPTL(s , n) for(int l=s;l<n;l++) const LL mod = 1000000; ///const int prime = 999983; const int INF = 0x3f3f3f3f; const int INFF = 1e9; const double pi = 3.141592653589793; const double inf = 1e18; const double eps = 1e-10; inline int read_int(){ int ret=0; char tmp; while(!isdigit(tmp=getchar())); do{ ret=(ret<<3)+(ret<<1)+tmp-'0'; } while(isdigit(tmp=getchar())); return ret; } /*******************************************/ typedef long long ll ; const int moddd = 1e9+7 ; int a[100005] ; const int maxn = (1 << 20) ; ll d[20][20] ; ll t[maxn] ; ll ji[maxn] ; int bin[25] ; const int wei = 20 ; int main(){ int jie = 1 ; for(int i=0;i<20;i++ , jie*=2){ bin[i] = jie ; } int n , m ; ///freopen("in.txt" , "r", stdin) ; int T ;scanf("%d" , &T) ; while(T --){ for(int j=0;j<maxn;j++) ji[j] = inf ; mem0(t) ; mem0(d) ; scanf("%d" , &n) ; for(int i=0;i<n;i++) scanf("%d" , &a[i]) ; ll tot = 0 ; int maxx = 0 ; for(int i=0;i<20;i++){ for(int j=0;j<n;j++){ if(a[j] & bin[i]) { maxx = i ; } } } for(int i=0;i<n-1;i++) { int t1 = a[i] ; int t2 = a[i+1] ; if(t1 > t2) swap(t1 , t2) ; tot += (t2 - t1) ; int tmp = t2 - t1 ; int ti = 0 ; for(int j=wei-1;j>=0;j--){ bool b1 = t2 & bin[j] ; bool b2 = t1 & bin[j] ; if(b1 != b2) { ti = j ;break ; } } for(int j=0;j<wei;j++){ d[ti][j] += abs((t2^bin[j])-(t1^bin[j])) - tmp ; } } ll ans = tot ; ll mii = 0 ; ll counter = 0 ; int upper = (1 << (maxx)) ; for(int i=0;i<upper;i++) t[i] = tot ; for(int i=0;i<wei;i++){ for(int x=0;x<upper;x++) ji[x] = inf ; for(int x=0;x<upper;x++){ ll tmp = 0 ; int tx = x % (bin[i] << 1) ; if(ji[tx] != inf) { t[x] += ji[tx] ; continue ; } else{ int flag = 0 ; if(x & bin[i]) flag = 1 , tmp += d[i][i] ; for(int j=0;j<wei;j++){ ///counter ++ ; if(x & bin[j]){ if(flag == 0) tmp += d[i][j] ; else if(j < i) tmp -= d[i][j] ; } } ji[tx] = tmp ; t[x] += tmp ; } } } for(int i=0;i<upper;i++){ if(t[i] < ans) { ans = t[i] ; mii = i ; } } //cout << "counter = " << maxn <<" "<< counter << endl ; cout << mii << " " << ans << endl ; } }