zoukankan      html  css  js  c++  java
  • 【题解】[省选联考 2021 A/B 卷] 滚榜

    题目大意

    已知序列 (A) 以及序列 (B) 的和 (m) 要求对每一个 (a_i) 分配一个 (b_i) 使得每次分配完之后 (a_i+b_i) 都是 (A) 序列中所有数字的最大值。

    每次修改完 (a_i o a_i+b_i).

    Solution:

    看到 (n) 的范围就会想到状压:设 (f[S,i,j,k]) 表示已经揭榜的人的集合为 (S,) 上一个揭榜的人是 (i,) 他的 (b)(j) 还剩下 (k) 的题目数能用。复杂度是 (O(2^{n}nm^2))

    那么看到后面那两个东西很不顺眼……和 (m) 有关的维度必须被砍掉。

    观察到题面的一条重要信息: (b_i)是单调不降的。

    那么这个东西怎么用呢?先暂且不谈,考虑一下我们维护 (b) 的实际意义有多大。

    我们维护它完全是因为后面的暴力转移需要它,它和我们最后的答案 一点关系都没有

    那么,我们考虑一下有多少方案是一样的:对于一个固定下来的排名,分配 (b) 的方式有很多,但是这种方案只有一个。

    从这里就能看出来维护 (b) 的冗余性质了。考虑对于一个已知的排名序列 (p,) (b) 的最优分配(即做到最小)就是每次尽量让 (b) 相等。

    那么每一个 (b) 的作用无非就是让 (p_{i+1}+b_{i+1}) 与前一项尽量接近,以此来保证我们选择的 (sum b) 最小。

    剩下的和 (m) 的差距都可以在最后一项补回来。

    但是值得注意的是,我们这样看似漏下很多东西,但实际上,这样分配(b) 所丢失的仅仅只是同一个排列下不同的 (b) 的排列方案,而这些方案数并不是我们所需要的。

    于是,我们可以思考题干中给定那个 (b) 不降的条件有啥用了。它也恰好规约了我们在揭榜一个人以后,后面所有人揭榜的增加量都是在 (b_i) 以上的。

    考虑对 (b) 进行差分,就是考虑我们在 (a_i) 的时候对它加上了多少。这样,每一个差分后的值 (c_i) 都会在后面的揭榜过程中对 (sum b) 贡献 ((n-i+1)c_i) 的值。

    这样我们就会发现,实际上我们已经不需要维护 (b_j) 了。只要知道当前状态下最后来的人和 pre-state 最后进来的人,就可以根据他们的 (a_i) 推算出最小代价了。因为我们只需要知道最小代价。

    至于为什么只需要最小代价……因为我们对每一种排名序列只需要求出一个 (sum b) 最小的就可以了。

    于是可以预处理 (cost[i][j]) 表示将 (a_i) 变成 (a_j) 的最小代价。

    注意一下题目中给的编号的细节。

    那么,现在的方程就可以被我们转化为: (f[S][i][j]) 表示揭榜的人状态为 (S,) 最后一个进来的人是 (i,) 一共用掉了 (j) 题的方案数。

    于是我们可以枚举状态,枚举 (i,pre) 从而转移。转移复杂度 (O(2^n n^2 m))

    注意用一些技巧优化,比如 lowbit

    回过头来想一下,这个题去掉 (b) 的限制,我们实际上用了什么样的思想?

    想到了差分,而这一步骤实际上是把每一个人的贡献给分开了——分成了对于全局的贡献。

    分开之后呢?我们发现,可以通过对代价的提前计算来求解这个题。

    于是,我们就可以把 (b) 这一维度放到 代价提前计算 这一部分中了。

    所以有一点启示:有时候可以考虑把不想要的状态提前计算掉。

    当然需要一些转化。

    最后提一下初始化的问题:每次滚榜之后的最大值一定是大于最初始序列的最大值的,而这个最大值也是原序列唯一一个有资格当榜上最大值的数,也就是说,它的所有榜一值上最小的。

    所以我们需要计算一个状态的最小值能不能满足条件,这同时也是最开始状态转移——转移到最初榜首的位置,这样才是最小的。这是最小代价,注意。

    考场时候没做出来 现在终于明白了)

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,a[200];
    int f[1<<14][14][501];
    int cost[14][14],maxn[14];
    int num[1<<14],pw[1<<14];
    inline int Max(int x,int y){return x>y?x:y;}
    inline int Min(int x,int y){return x<y?x:y;}
    inline int lowbit(int x){return x&(-x);}
    inline int pop_count(int x){
    	int res=0;
    	for(int j=x;j;j-=lowbit(j))res++;
    	return res;
    }
    void solve(){
    	scanf("%lld%lld",&n,&m);
    	for(int i=0;i<=13;++i)pw[1<<i]=i;
    	for(int i=0;i<n;++i)scanf("%lld",&a[i]);
    	for(int i=0;i<n;++i)
    		for(int j=0;j<n;++j){
    			//min cost{i	o j}
    			cost[i][j]=Max(a[j]-a[i]+(i>j),0);
    			maxn[i]=Max(maxn[i],cost[i][j]);
    		}
    	for(int i=0;i<(1<<n);++i)num[i]=pop_count(i);
    	for(int i=0;i<n;++i)if(m>=n*maxn[i])f[1<<i][i][n*maxn[i]]=1;//mean that 'i' can be any number
    	//最优策略下的数字一定最后不高于变成最大值的代价,所以初始化只能处理掉变成最大值所消耗的代价 
    	for(int i=1;i<(1<<n);++i){
    		if(num[i]<=1)continue;
    		for(int j=i;j;j-=lowbit(j)){
    			int state=i-lowbit(j);//state/j
    			int pre=pw[lowbit(j)];//pos_j
    			for(int k=state;k;k-=lowbit(k)){
    				int pstate=lowbit(k);//1<<p
    				int pos=pw[pstate];
    				if(pos==pre)continue;
    				int g=cost[pre][pos]*(n-num[state]);
    				for(int v=g;v<=m;++v){
    					f[i][pre][v]+=f[state][pos][v-g];
    				}
    			}
    		}
    	}
    	int ans=0;
    	for(int i=0;i<n;++i)
    		for(int j=0;j<=m;++j)
    			ans+=f[(1<<n)-1][i][j];
    	cout<<ans<<endl;
    }
    signed main(){
    	freopen("111.txt","r",stdin);
    	solve();
    	return 0;
    }
    
  • 相关阅读:
    oracle 工作中遇到的函数
    算法工作中使用的
    Windows下用PIP安装scipy出现no lapack/blas resources found
    Spring mvc 加载HTML静态页面
    Restful架构学习
    Python: Windows下pip安装库出错:Microsoft Visual C++ 9.0 is required < Unable to find vcvarsall.bat
    局域网内部访问问题
    安装mysql-connector-python
    Centos 安装 python2.7.10以及pip
    python2.7安装pip遇到ImportError: cannot import name HTTPSHandle
  • 原文地址:https://www.cnblogs.com/h-lka/p/15005257.html
Copyright © 2011-2022 走看看