基于单调序列的数位动态规划
|Word count:857|Reading time:4min|Post View:
题解
定义序列$a_{1…n}$的权值为$\sum a_i$
求满足$l_i \leq a_i \leq r_i$且$a_i$单调不降的所有序列$a_{1…n}$的权值和。
考虑最高位,那么一定存在一个$k$,使得$a[1…k]$最高位是$0$,$a[k+1…n]$最高位是$1$,那么可以分成两个子区间去做。
设$f[i][L][R][LIML][LIMR]$表示:在$i+1…60$位已经定下来的情况下(此时$a[L…R]$的$i+1..60$位的值一样),区间$[L…R]$只考虑$1…i$位的答案。
$LIML$表示只考虑$i+1..60$位$a[L…R]$是否等于区间中最大的$l_x$;
$LIMR$表示只考虑$i+1..60$位$a[L…R]$是否等于区间中最小的$r_x$
!注意!:对于异或问题,存在单调性的问题,利用数位动态规划的思想根据最高位取值为$0/1$将集合划分为两部分进行分治是很常用的做法。
代码
struct atom{ int sumw,way; atom(int _sumw=0,int _way=0){ sumw=_sumw; way=_way; } friend atom operator +(atom a,atom b){ return atom((a.sumw+b.sumw)%mod,(a.way+b.way)%mod); } friend atom operator *(atom a,atom b){ return atom((a.sumw*1ll*b.way+a.way*1ll*b.sumw)%mod,a.way*1ll*b.way%mod); } };
void Solve(){ for(int i=1;i<=n;++i){ mir[i][i]=r[i]; mal[i][i]=l[i]; for(int j=i+1;j<=n;++j){ mir[i][j]=min(mir[i][j-1],r[j]); mal[i][j]=max(mal[i][j-1],l[j]); } } for(int l=1;l<=n;++l) for(int r=l;r<=n;++r) for(int el=0;el<2;++el) for(int er=0;er<2;++er){ f[0][l][r][el][er]=atom(0,1); } for(int i=1;i<=60;++i){ for(int l=1;l<=n;++l) for(int r=l;r<=n;++r) for(int el=0;el<2;++el) for(int er=0;er<2;++er){ atom ans=atom(0,0); for(int k=l;k<=r-1;++k){ int lel=(el&&(mal[l][k]>>i)==(mal[l][r]>>i)); int ler=(er&&(mir[l][k]>>i)==(mir[l][r]>>i)); int rel=(el&&(mal[k+1][r]>>i)==(mal[l][r]>>i)); int rer=(er&&(mir[k+1][r]>>i)==(mir[l][r]>>i)); if(lel&&(mal[l][k]&(1ll<<(i-1))))continue; if(rer&&(!(mir[k+1][r]&(1ll<<(i-1)))))continue; ler&=((mir[l][k]&(1ll<<(i-1)))==0); rel&=((mal[k+1][r]&(1ll<<(i-1)))>0); ans=(ans+f[i-1][l][k][lel][ler]*f[i-1][k+1][r][rel][rer]*atom((r-k)*1ll*((1ll<<(i-1))%mod)%mod,1)); } if(el&&(mal[l][r]&(1ll<<(i-1)))); else{ int nl=el; int nr=er&&((mir[l][r]&(1ll<<(i-1)))==0); ans=(ans+f[i-1][l][r][nl][nr]*atom(0,1)); } if(er&&(!(mir[l][r]&(1ll<<(i-1))))); else{ int nl=el&&(mal[l][r]&(1ll<<(i-1))); int nr=er; ans=(ans+f[i-1][l][r][nl][nr]*atom((r-l+1)*1ll*((1ll<<(i-1))%mod)%mod,1)); } f[i][l][r][el][er]=ans; } } printf("%d\n",f[60][1][n][1][1].sumw); }
|