标签:长度 sig ons space 如何 getc 反转 typedef its
考虑区间DP。设\(L[i][j]\)表示当前装置里的数字是\(i-1\),已经确定的答案区间为\([i,j]\),还需要的最少操作次数;\(R[i][j]\)表示当前装置里的数字是\(j+1\),已经确定的答案区间为\([i,j]\),还需要的最少操作次数。
发现左端点相同时答案随区间长度的增加而单调不降,且值域很小(实验表明不超过\(45\)),所以考虑将DP的值域和下标反转。
现在重新定义\(L[v][i]=j\)表示用不超过\(v\)的代价,当前装置里的数字为\(i\)时,向右能询问出的最长的区间为\([i+1,j]\)。也就是最大的一个\(j\)使得原DP数组的值\(\leq v\)。类似地,定义\(R[v][i]=j\)表示当前装置里的数字为\(i\)时,向左能询问出的最长的区间为\([j,i-1]\)。
考虑转移。我们按\(v\)的大小分层转移。考虑当前在\(v\)这一层,用刷表法去更新\(v+cost\)这些层。
以\(L\)的转移为例:
\[
L[v][i]\rightarrow L[v+dis(j,i)+1][j]\quad (i\geq j,R[v][i]\leq j+1)
\]
这个式子的意思是,如果满足括号里的条件,就让\(L[v+dis(j,i)+1][j]\)对\(L[v][i]\)取\(\max\)。其中\(dis(j,i)\)表示把装置里的数字从\(j\)改到\(i\)需要的最少操作次数。之所以要求\(R[v][i]\leq j+1\),是因为假如当前我们在\(i\)这个位置,手里还剩\(v\)次操作时,如果\(R[v][i]>j+1\),即用\(v\)次操作无法问到区间的左端点\(j+1\),那此时小Y决策时就会把数字故意安排在左边(\(<i\))。在这种情况下,鸽子无法通过把数字从\(j\)改到\(i\),以此在\(v+dis(i,j)+1\)次操作内问出\([j+1,L[v][i]]\)这个区间,也就无法进行这次转移。
朴素的转移是\(O(45\cdot n^2)\)的(\(n=99999\)),考虑优化这个复杂度。
我们枚举\(v\),再枚举\(j\),考虑如何维护出能转移到当前\(j\)的所有\(i\)中的最优解。我们维护一个桶,表示装置里的数字从\(j\)变到\(i\),修改了哪些位,其他没有修改的位又分别是多少。由于每个数码有\(0\sim 9\)共\(10\)种取值,我们还要加一个符号表示这一位被修改了,所以这个桶的大小是\(11^5\)。考虑从小到大扫描所有的\(j\),每次把\(R[v][i]=j+1\)的所有\(i\)取出。再枚举从\(j\)变到\(i\)时哪些位被修改了(共\(2^5\)种情况),其它没被修改的位保留\(i\)本身的数字,这样就得到了一个值域在\([0,11^5)\)之间的状态。用\(L[v][i]\)的值去更新对应状态的桶。然后对于当前\(j\),桶里已经存下了对于每个被修改的位的集合,从这些\(i\)转移到\(j\)的最优解。我们直接用这个最优解去更新\(L[v+dis(j,i)+1][j]\)即可。
关于这个桶的部分可以结合代码与注释理解。
这样,每一层转移的复杂度是\(O(2^5\cdot n+11^5)\)。其中\(11^5\)是清空上一层的桶所需要的代价。故总时间复杂度为\(O(45\cdot(2^5\cdot n+11^5))\)。
参考代码:
//problem:nflsoj552
#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fst first
#define scd second
typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
if(S==T){
T=(S=buf)+fread(buf,1,MAXN,stdin);
if(S==T)return EOF;
}
return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#endif
inline int read(){
int f=1,x=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline ll readll(){
ll f=1,x=0;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
/* ------ by:duyi ------ */ // dysyn1314
inline void cmax(int &x,int y){x=(y>x)?y:x;}
inline void cmin(int &x,int y){x=(y<x)?y:x;}
const int MAXN=99999;
int F[50][MAXN+5],L[50][MAXN+5],R[50][MAXN+5],maskid[MAXN+5][1<<5],lm[161051];
vector<int>buc[MAXN+5];
/*
maskid[i][s]: i这个数,修改了s这些数位后,达到的状态的编号
对两个数(x,y)之间的修改,
我们视作先把x的某些位抹掉(改成一种0~9以外的字符,表示临时状态),
再把这些临时位改成y对应的数位.
当然,虽然把dis(x,y)拆成了两次操作,我们统计答案时还是只计算1次.
这样拆只是为了方便转移,有点meet in middle的感觉.
这样,0~9,加上临时字符,一共11个字符,有11^5种状态,我们用一个桶存起来,
而这个桶的下标就是"状态编号",即maskid
*/
void DP(){
memset(F,0xbf,sizeof(F));
memset(L,0xbf,sizeof(L));
memset(R,0x3f,sizeof(R));
for(int i=0;i<=MAXN;++i){
F[0][i]=L[0][i]=min(MAXN,i+1);
R[0][i]=max(0,i-1);
static int w[5];
for(int j=0,t=i;j<5;++j){
w[j]=t%10,t/=10;
}
for(int s=0;s<32;++s){
int cur=0;
for(int j=0;j<5;++j){
int t=w[j];
if(s&(1<<j))t=10;
cur=cur*11+t;
}
maskid[i][s]=cur;
}
}
for(int v=0;v<45;++v){
memset(lm,0xbf,sizeof(lm));
for(int i=0;i<=MAXN;++i)buc[i].clear();
for(int i=0;i<=MAXN;++i)buc[R[v][i]].pb(i);
for(int s=0;s<32;++s){
int nv=v+__builtin_popcount(s)+1;
if(nv>45)continue;
for(int i=0;i<(int)buc[0].size();++i){
cmax(lm[maskid[buc[0][i]][s]],L[v][buc[0][i]]);
}
for(int i=0;i<=MAXN;++i){
if(i!=MAXN)for(int j=0;j<(int)buc[i+1].size();++j){
cmax(lm[maskid[buc[i+1][j]][s]],L[v][buc[i+1][j]]);
}
cmax(L[nv][i],lm[maskid[i][s]]);
cmax(F[nv][i],lm[maskid[0][s]]);
}
}
memset(lm,0x3f,sizeof(lm));
for(int i=0;i<=MAXN;++i)buc[i].clear();
for(int i=0;i<=MAXN;++i)if(L[v][i]>=0)buc[L[v][i]].pb(i);
for(int s=0;s<32;++s){
int nv=v+__builtin_popcount(s)+1;
if(nv>45)continue;
for(int i=0;i<(int)buc[MAXN].size();++i){
cmin(lm[maskid[buc[MAXN][i]][s]],R[v][buc[MAXN][i]]);
}
for(int i=MAXN;i>=0;--i){
if(i!=0)for(int j=0;j<(int)buc[i-1].size();++j){
cmin(lm[maskid[buc[i-1][j]][s]],R[v][buc[i-1][j]]);
}
cmin(R[nv][i],lm[maskid[i][s]]);
}
}
}
}
int main() {
DP();
cerr<<"ok"<<endl;
int T=read();while(T--){
int L=read(),R=read(),ans=0;
--L;
while(F[ans][L]<R)ans++;
cout<<ans<<endl;
}
return 0;
}
标签:长度 sig ons space 如何 getc 反转 typedef its
原文地址:https://www.cnblogs.com/dysyn1314/p/12388421.html