• [Codeforces477D]Dreamoon and Binary


    Problem

    给定一个字符串数的二进制表示(不含前导0)s(长度不超过5000),
    对于一个数n(初值为0),可以进行以下两种操作:
    1.将n的二进制表示(无前导0)写到已经写的串的后面.
    2.n加上1.
    问组成s的不同方法数以及最少用多少次操作能组成串s.

    Solution

    对于第一问:
    用f[i][j]表示最后一个数是j+1到i的方案数,g[i][j]表示操作1的个数
    所以f[i][j]+=f[j][k](k < j < i 且 k到j的数小于j到i的数) ,因为如果k到j的位数比j到i的位数少,则k到j的数必定小于j到i的数
    所以可以用前缀和优化,再在位数相等时特判一下

    对于第二问:
    我们可以知道,如果第二个操作的次数都大于总位数了,那么便是没有意义的
    所以在有数的情况下,我们只要枚举后16位的转移,这是不会爆int的
    但如果后16位的转移没法转移(不能有前导0),那么我们需要找到最后一个可以转移的地方来转移
    那么所有的操作数就是最后的那个数加上操作1的个数

    Notice

    判断大小要用O(1),先预处理一下

    Code

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define sqz main
    #define ll long long
    #define reg register int
    #define rep(i, a, b) for (reg i = a; i <= b; i++)
    #define per(i, a, b) for (reg i = a; i >= b; i--)
    #define travel(i, u) for (reg i = head[u]; i; i = edge[i].next)
    const int INF = 1e9, mo = 1e9 + 7, N = 5005;
    const double eps = 1e-6, phi = acos(-1.0);
    ll mod(ll a, ll b) {if (a >= b || a < 0) a %= b; if (a < 0) a += b; return a;}
    ll read(){ ll x = 0; int zf = 1; char ch; while (ch != '-' && (ch < '0' || ch > '9')) ch = getchar();
    if (ch == '-') zf = -1, ch = getchar(); while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); return x * zf;}
    void write(ll y) { if (y < 0) putchar('-'), y = -y; if (y > 9) write(y / 10); putchar(y % 10 + '0');}
    int f[N][N], g[N][N], f_sum[N][N], g_min[N][N], T[N], S[N], mi[N], Same[N][N];
    char st[N];
    int cmp(int b1, int b2, int len)
    {
    	int t = Same[b1][b2];
    	if (t >= len) return 1;
    	return T[b1 + t] < T[b2 + t];
    }
    int sqz() 
    {
    	scanf("%s", st + 1);
    	int n = strlen(st + 1);
    	rep(i, 1, n) T[i] = st[i] - '0';
    	mi[0] = 1;
    	rep(i, 1, n)
    	{
    		S[i] = (S[i - 1] * 2 + T[i]) % mo;
    		mi[i] = mi[i - 1] * 2 % mo;
    	} 
    	per(i, n, 1)
    		per(j, n, 1)
    			if (T[i] == T[j]) Same[i][j] = Same[i + 1][j + 1] + 1;
    			else Same[i][j] = 0;
    	rep(i, 1, n)
    	{
    		f[i][0] = f_sum[i][0] = g[i][0] = g_min[i][0] = 1;
    		rep(j, 1, i - 1)
    		{
    			g[i][j] = g_min[i][j] = INF;
    			if (T[j + 1] == 1)
    			{
    				int k = j - (i - j);
    				if (k < -1) k = -1;
    				f[i][j] = (f[i][j] + f_sum[j][k + 1]) % mo;
    				g[i][j] = min(g[i][j], g_min[j][k + 1] + 1) % mo;
    				if (k + 1 && T[k + 1] && cmp(k + 1, j + 1, i - j))
    				{
    					f[i][j] = (f[i][j] + f[j][k]) % mo;
    					g[i][j] = min(g[i][j], g[j][k] + 1) % mo;
    				}
    				f_sum[i][j] = (f_sum[i][j] + f[i][j])  % mo;
    				g_min[i][j] = min(g_min[i][j], g[i][j]);
    			}
    		}
    		g_min[i][i] = INF;
    		per(j, i - 1, 0)
    		{
    			f_sum[i][j] = (f_sum[i][j] + f_sum[i][j + 1])% mo;
    			g_min[i][j] = min(g_min[i][j], g_min[i][j + 1]);
    		}
    	}
    	int ans = INF;
    	rep(i, 1, min(16, n)) ans = min(ans, g[n][n - i] + (int)((S[n] - (ll)S[n - i] * mi[i]) % mo + mo) % mo) % mo;
    	if (ans == INF)
    		rep(i, 17, n)
    			if (g[n][n - i] != INF)
    			{
    				ans = g[n][n - i] + (int)((S[n] - (ll)S[n - i] * mi[i]) % mo + mo) % mo;
    				break;
    			}
    	printf("%d
    %d
    ", f_sum[n][0], ans);
    }
    
  • 相关阅读:
    HDU 1176 免费馅饼(DP)
    微通道的兵锋 阿里将血腥
    head first c&lt;11&gt;在根据网络编程
    ZOJ 3795 Grouping(Tarjan收缩点+DAG)
    【设计和算法分析】3、二进制搜索
    杨:联想的创新将成为打造最酷的助推力的重要器件
    android 如何将电话簿SDN数字和其他普通的数字混合在一起?
    redhat6.3已安装was6.1你可以不弹出安装程序
    Android属于查询执行情况的电话号码
    table在 点击线条颜色
  • 原文地址:https://www.cnblogs.com/WizardCowboy/p/7695073.html
Copyright © 2020-2023  润新知