题目链接

\(x:\ 11010011\)

\(y:\ 10011110\)

(下标是从高位往低位,依次是\(1,2,...,n\))

比如对于这两个数,先找到最高的满足\(x\)是\(0\),\(y\)是\(1\)的一位\(j\),显然我们还要找比\(i\)高的最近的一位\(i\),满足第\(i\)位\(x\)为\(1\),\(y\)为\(0\)。

然后我们要将\(x\)中\(i\)之后的位上的\(1\)全变成\(0\),然后\(x\)-=\(1\),才能使得\(x\)在\(j\)这一位为\(1\)。这样\(x\)的第\(i\)位变为\(0\),之后全变为\(1\),显然此时还需要的代价就是 \(n-i-\ y的后i位中1的个数\)。

而之前的代价就是 \(x后i位中1的个数\)。再算上\(i\)及前\(i\)位的代价,记\(cnt(x)\)为\(x\)的二进制表示中\(1\)的个数,\(x\)变成\(y\)的总代价其实就是 \(cnt(x)-cnt(y)+n-i\)。

这样就可以数位DP了。

\(f[i][las][0/1][0/1][0/1]\)表示 当前考虑到第\(i\)位,最近的满足\(x\)是\(1\),\(y\)是\(0\)的位是\(las\),是否处于上界,是否\(x\)已经大于\(y\)(要保证\(x\geq y\)),是否已统计过答案(只在最高的那位\(j\)统计答案),此时的总答案。

同样还需要记\(g[i][las][0/1][0/1][0/1]\)表示方案数,用来统计答案。

转移时枚举\(x,y\)当前位填什么就行了。

状态数\(O(n^2)\)。代码在这儿,博文最下面也有。


也有\(O(n)\)的解法(我用记搜写了下,代码在这儿,博文最下面也有):

orz \(tangjz\)的题解后,发现\(las\)这一维很容易就省去了...

原本记\(las\)是为了计算\(n-i\)这部分贡献,放到代码中就是\(ans\)+=\(g*(n-i)\)(\(g\)是方案数)。

现在我们只记是否出现过\(i,j\)(状态是三进制,分别表示未出现\(i,j\)、出现过\(i\)还没出现\(j\)、\(i,j\)都出现了),如果出现过\(i\),转移的时候就\(ans\)+=\(g\)。

这样对于\(i\),\(g\)的贡献还是会算\(n-i\)次。

这样复杂度就是\(O(n)\)了。


解释一下\(tangjz\)的代码。。

写的从低位往高位的递推,每次枚举当前要计算的位\(i\),然后枚举\(j,k\)(\(j\)是上界,\(k\)是那个三进制),据此枚举(要满足这个状态)第\(i\)位\(x,y\)填\(0\)还是\(1\)。

因为是从低位往高位(要注意转移对状态),所以\(k=2\)是说还没有找到\(v\);\(k=1\)是指找到了\(v\)但没有确定\(u\);\(k=0\)是指\(u,v\)都已经确定了。

那么看转移,\(j\)这维和记搜写法是一样的...(其实哪一维和记搜都差不多)。

下面忽略\(j\)这一维,只考虑\(k\)。为了不混用,下面的\(u,v\)就是最上面\(x\)变成\(y\)这一过程中的\(i,j\),代价还是\(cnt[x]+cnt[y]+n-u\)。

\(k=0\),\(f[i][0]\)可以\(f[i-1][0],f[i-1][1]\)转移。但要从\(f[i-1][1]\)转移就是令第\(i\)位为\(u\),也就是需满足\(x=1,y=0\),此时既可以\(f[i-1][0]\)也可以从\(f[i-1][1]\)转移。

\(k=1\),\(f[i][1]\)可以\(f[i-1][1],f[i-1][2]\)转移。同理要从\(f[i-1][2]\)转移就是令第\(i\)位为\(v\),也就是要满足\(x=0,y=1\),且此时只能从\(f[i-1][2]\)转移。

上面两个转移虽然形式像但是不同。

\(k=2\),\(f[i][2]\)只能从\(f[i-1][2]\)转移。

当\(k=1\)或\(2\)时,也就是\(u\)还没出现,此时\(ans\)+=\(g\),这样就能统计\(n-u\)这个答案了(后面n-u位中,算每位的时候答案都加上一个当前的方案数g',合起来就是那个(n-u)*g)。

可能还会不懂...我再写一下:

假设有三位\(1,2,3\),现在考虑第\(1\)位,本来是在这里加\((n-1)*g[2]\),\(g[2]\)是填\(2\)及后面的位的方案数,\(g[2]\)就等于\(\sum g[3]\),所以\((n-1)=2,2*g[2]\)就是\(g[2]+\sum g[3]\)。

所以你在\(g[2]\)统计一次,每个\(g[3]\)那统计一次,就是\(g[2]+\sum g[3]=2*g[2]=(n-1)*g[2]\)。

再扩展几位也是这样。

还不懂的话看代码吧...感觉说不太清楚啊QAQ


\(O(n^2)\):

//0.68s	16.9MB
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define mod 1000000007
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
typedef long long LL;
const int N=505; int A[N];
pr f[N][N][8];
char s[N]; pr DFS(int x,int las,int s)//s:lim,f1,f2 f1:是否已x>y f2:是否已统计过答案
{
if(!x) return mp(0,1);
if(f[x][las][s].first!=-1) return f[x][las][s];
LL res1=0,res2=0;
int lim=s&1, f1=s>>1&1, f2=s>>2&1;
for(int i=0; i<2; ++i)
for(int j=0; j<2; ++j)
{
if(lim && i>A[x]) continue;
if(!f1 && i<j) continue;
pr v=DFS(x-1,i>j?x:las,(lim&&i==A[x])|((f1||i>j)<<1)|((f2||i<j)<<2));
res1+=v.first, res2+=v.second;
if(i) res1+=v.second;
if(j) res1+=mod-v.second;
if(!f2 && i<j) res1+=1ll*v.second*(las-1)%mod;
}
return f[x][las][s]=mp(res1%mod,res2%mod);
} int main()
{
// freopen("yjqaa.in","r",stdin);
// freopen("yjqaa.out","w",stdout); scanf("%s",s+1); int n=strlen(s+1);
std::reverse(s+1,s+1+n);
for(int i=1; i<=n; ++i) A[i]=s[i]-'0';
for(int i=1; i<=n; ++i)
for(int j=0; j<=n; ++j)
f[i][j][0].first=f[i][j][1].first=f[i][j][2].first=f[i][j][3].first=
f[i][j][4].first=f[i][j][5].first=f[i][j][6].first=f[i][j][7].first=-1;
// f[i][j][0][0][0].first=f[i][j][0][0][1].first=f[i][j][0][1][0].first=f[i][j][0][1][1].first=
// f[i][j][1][0][0].first=f[i][j][1][0][1].first=f[i][j][1][1][0].first=f[i][j][1][1][1].first=-1;
printf("%d\n",DFS(n,0,1).first); return 0;
}

\(O(n)\):

//4ms	488KB
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mp std::make_pair
#define pr std::pair<int,int>
#define mod 1000000007
#define Add(x,v) (x+=v)>=mod&&(x-=mod)
typedef long long LL;
const int N=505; int A[N];
pr f[N][2][3];
char s[N]; void operator +=(pr &x,pr y)
{
Add(x.first,y.first), Add(x.second,y.second);
}
pr DFS(int x,int lim,int s)//s:0/1/2
{
if(!x) return s==1?mp(0,0):mp(0,1);
if(f[x][lim][s].first!=-1) return f[x][lim][s];
LL res1=0,res2=0;
const int xL=0, xR=lim?A[x]:1;
for(int i=xL; i<=xR; ++i)
{
const int yL=s==1?i:0, yR=!s?i:1;
for(int j=yL; j<=yR; ++j)
{
pr v;
if(!s)
{
v=DFS(x-1,lim&&i==xR,0);
if(i && !j) v+=DFS(x-1,lim&&i==xR,1);
}
else if(s==1)
{
if(!i && j) v=DFS(x-1,lim&&i==xR,2);
else v=DFS(x-1,lim&&i==xR,1);
}
else v=DFS(x-1,lim&&i==xR,2);
res1+=v.first, res2+=v.second;
// if(i) res1+=v.second;
// if(j) res1+=mod-v.second;
// if(s) res1+=v.second;
res1+=(i-j+(s>0))*v.second;
}
}
return f[x][lim][s]=mp(res1%mod,res2%mod);
} int main()
{
// freopen("yjqaa.in","r",stdin);
// freopen("yjqaa.out","w",stdout); int T; scanf("%d",&T);
while(T--)
{
scanf("%s",s+1); int n=strlen(s+1);
std::reverse(s+1,s+1+n);
for(int i=1; i<=n; ++i) A[i]=s[i]-'0';
for(int i=1; i<=n; ++i)
f[i][0][0].first=f[i][0][1].first=f[i][0][2].first=
f[i][1][0].first=f[i][1][1].first=f[i][1][2].first=-1;
printf("%d\n",DFS(n,1,0).first);
} return 0;
}

字节跳动冬令营网络赛 D.The Easiest One(贪心 数位DP)的更多相关文章

  1. 字节跳动冬令营网络赛 Solution

    A:Aloha Unsolved. B:Origami Unsolved. 题意: 初始的时候有一张纸,可以从左边往右边折叠,或者从右边往左边折叠 每次折叠的长度不能超过现有宽度,最后折叠到长度为1 ...

  2. 牛客网字节跳动冬令营网络赛J Sortable Path on Tree —— 点分治

    题目:https://ac.nowcoder.com/acm/contest/296/J 用点分治: 记录了值起伏的形态,二元组 (x,y) 表示有 x 个小于号,y 个大于号: 因为小于号和大于号都 ...

  3. 2017年icpc西安网络赛 Maximum Flow (找规律+数位dp)

    题目 https://nanti.jisuanke.com/t/17118 题意 有n个点0,1,2...n-1,对于一个点对(i,j)满足i<j,那么连一条边,边权为i xor j,求0到n- ...

  4. 2019字节跳动冬令营day7娱乐赛19题题解

    啊没去听讲题,也没发纸质题解,电子版题解也没有 为最后几个unsolve自闭了一段时间才全都A掉 3个队友写的我没看的题通过人数蛮多就不管了 题目地址:https://pan.baidu.com/s/ ...

  5. ACM-ICPC2018南京网络赛 AC Challenge(一维状压dp)

    AC Challenge 30.04% 1000ms 128536K   Dlsj is competing in a contest with n (0 < n \le 20)n(0<n ...

  6. HDU 6438 网络赛 Buy and Resell(贪心 + 优先队列)题解

    思路:维护一个递增队列,如果当天的w比队首大,那么我们给收益增加 w - q.top(),这里的意思可以理解为w对总收益的贡献而不是真正获利的具体数额,这样我们就能求出最大收益.注意一下,如果w对收益 ...

  7. 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛 H. Skiing (拓扑排序+假dp)

    题目链接:https://nanti.jisuanke.com/t/16957 题目: In this winter holiday, Bob has a plan for skiing at the ...

  8. HDU 5886 Tower Defence(2016青岛网络赛 I题,树的直径 + DP)

    题目链接  2016 Qingdao Online Problem I 题意  在一棵给定的树上删掉一条边,求剩下两棵树的树的直径中较长那的那个长度的期望,答案乘上$n-1$后输出. 先把原来那棵树的 ...

  9. 2016 acm香港网络赛 C题. Classrooms(贪心)

    原题网址:https://open.kattis.com/problems/classrooms Classrooms The new semester is about to begin, and ...

随机推荐

  1. java----DOS命令

    dir /?   查看帮助 dir /s   查看当前的目录,以及子目录

  2. django----Form实时更新两种方式

    在ModelForm需要知道: from app03 import models from django.forms import ModelForm class UserForm(ModelForm ...

  3. CF939F

    好神奇的dp... 首先有一个很简单的思想:设dp[i][j]表示目前到了第i分钟,朝上的面被烤了j分钟的情况下所需的最小交换次数 那么有转移:dp[i][j]=min(dp[i-1][j],dp[i ...

  4. lisp : set 与setq 函数

    在Lisp中,如果我们希望对一个变量赋值,可以使用set函数,用法如下: (set ‘my-value "my string") 上面的代码是对变量my-value进行赋值,值是& ...

  5. 论文阅读笔记三:R2CNN:Rotational Region CNN for Orientation Robust Scene Text Detection(CVPR2017)

    进行文本的检测的学习,开始使用的是ctpn网络,由于ctpn只能检测水平的文字,而对场景图片中倾斜的文本无法进行很好的检测,故将网络换为RRCNN(全称如题).小白一枚,这里就将RRCNN的论文拿来拜 ...

  6. 眼底血管分割训练函数(SVM,Adaboost)

    # -*- coding: utf-8 -*- import numpy as np from sklearn import svm from sklearn.model_selection impo ...

  7. Just oj 2018 C语言程序设计竞赛(高级组)H: CBT?

    H: CBT? 时间限制: 1 s      内存限制: 128 MB      提交 我的状态 题目描述 对于二叉树,如果这棵树的节点排布是按行从上到下,每行从左到右挨个放置,中间不会有空闲的节点. ...

  8. 关于The specified Android SDK Build Tools version (26.0.2) is ignored, as it is below the minimum...

    今天将项目迁移到另一台笔记本,进行build出现以下问题,导致build失败 The specified Android SDK Build Tools version (26.0.2) is ign ...

  9. Caused by: java.net.ConnectException: Connection refused/Caused by: java.lang.RuntimeException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

    1.使用sqoop技术将mysql的数据导入到Hive出现的错误如下所示: 第一次使用命令如下所示: [hadoop@slaver1 sqoop--cdh5.3.6]$ bin/sqoop impor ...

  10. Personal小金库(避免遗忘,优秀的网址会保存于此方便自己查看)

    由于记性不好,~.~,所以整理了一下一些自己经常看的网址或者博客......不断更新中,如果对您造成了侵权,我立马删除.谢谢~.~ 1:个人的一些link~.~ 博客园名称:别先生 博客园网址:htt ...