多校联合训练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 ;
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值