题目链接

\(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. PyCharm新建.py文件时自动带出指定内容

    如:给Pycharm加上头行  # coding:utf-8File—Setting—Editor--Code Style--File and Code Templates--Python Scrip ...

  2. Jmeter 自动化测试报告扩展

    首先了解下生成测试报告的过程,我们看到的测试报告是由.jtl格式转换为.html,html报告的样式由extras目录下xsl文件决定.优化测试报告需要分为两部分内容,首先我们要优化输出的测试内容,其 ...

  3. windows下bat批处理执行sql语句__Mysql

    直接上代码: @ECHO OFF SET dbhost=主机名(例如:127.0.0.1)SET dbuser=用户名(例如:root)SET dbpasswd=用户密码(例如:root)SET db ...

  4. 读C#开发实战1200例子记录-2017年8月14日11:20:38获取汉字编码值

    try { char chr = textBox1.Text[0]; byte[] gb2312_bt = Encoding.GetEncoding("gb2312").GetBy ...

  5. windows解压.tar00文件

    通常是单个文件太大分拆出来的,例如data.tar00, data.tar01, data.tar02等 cmd命令行进入几个tar0x文件所在目录,执行: copy /b data.tar0* da ...

  6. .Net(C#)用正则表达式清除HTML标签(包括script和style),保留纯本文(UEdit中编写的内容上传到数据库)

    去官网下载,本Demo用的MVC模式 下载地址:http://ueditor.baidu.com/website/download.html 加入文件夹中的结构: 引入了函数公式的图标: @{ Vie ...

  7. asp.net core 缓存和Session

    缓存 缓存在内存中 ASP.NET Core 使用 IMemoryCache内存中缓存是使用依赖关系注入从应用中引用的服务. 请在ConfigureServices中调用AddMemoryCache( ...

  8. Oracle数据库表索引失效,解决办法:修改Oracle数据库优化器模式

    ALTER SYSTEM SET OPTIMIZER_MODE=RULE scope=both; 其他可以选择的模式还有ALL_ROWS/CHOOSE/FIRST_ROWS/ALL_ROWS. 应用系 ...

  9. Visual Studio中的.suo(Solution User Options)文件

    其实关于.suo文件,官方文档和网上很多资料就说明的十分详细了,本文主要按照我自己的理解将其整理归纳成一篇笔记以备日后查看..suo文件全称为:Solution User Options,看了很多资料 ...

  10. Json对象处理.将对象处理成dic数组.

    var parser = new JsonConfigurationFileParser();var dict = parser.Parse("json"); using Newt ...