首页 > 试题广场 >

小红的01子序列构造(hard)

[编程题]小红的01子序列构造(hard)
  • 热度指数:1285 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 512M,其他语言1024M
  • 算法知识视频讲解
\hspace{15pt}小红希望你构造一个长度为 n\tt 01 串,满足 m 个性质:对于 i \in [1, m] ,区间 [l_i, r_i] 有恰好 x_i\tt 0y_i\tt 1k_i\tt 01 子序列
\hspace{15pt}给定的限制区间为两两包含关系。即对于 i \neq j ,若 l_i \leq l_j ,则 r_i \geq r_j ,反之亦然。

\hspace{15pt}子序列为从原字符串中删除任意个(可以为零、可以为全部)字符得到的新字符串。

输入描述:
\hspace{15pt}第一行输入两个正整数 n,m \left(1\leq n,m \leq 10^5\right) 代表 \tt 01 串的长度、限制的数量。
\hspace{15pt}接下来的 m 行,每行输入五个非负整数 l_i,r_i,x_i,y_i,k_i \left(1\leq l_i \leq r_i \leq n;\ 0\leq k_i \leq 10^{10};\ 0\leq x_i,y_i \leq r_i-l_i+1;\ x_i+y_i=r_i-l_i+1\right) ,代表构造的 \tt 01 串需满足区间 [l_i,r_i] 有恰好 x_i\tt 0y_i\tt 1k_i\tt 01 子序列。
\hspace{15pt}保证给定的任意两个区间均为互相包含关系。


输出描述:
\hspace{15pt}如果答案不存在,直接输出 -1 ;否则,在一行上输出一个长度为 n\tt 01 串。

\hspace{15pt}如果存在多个解决方案,您可以输出任意一个,系统会自动判定是否正确。注意,自测运行功能可能因此返回错误结果,请自行检查答案正确性。
示例1

输入

4 2
1 4 2 2 4
2 3 1 1 1

输出

0011
示例2

输入

4 2
1 4 2 2 3
2 3 1 1 1

输出

-1
wonima 题目都看不懂
发表于 2025-05-06 11:20:37 回复(0)
这是给人做的题?
发表于 2025-04-14 18:48:43 回复(0)
三国杀策划来出题了?
发表于 2025-08-29 15:31:04 回复(0)
package com.haust.hj;

import java.util.*;
/**

核心关系式:
若两个相邻区间:[a,b](有xa个0,ya个1,ka个01子序列), [b+1,c](有xb个0,yb个1,kb个01子序列)
则合成的新区间:[a,c]有ka+kb+xa*yb个01子序列.
若已知一个区间的0个数和1个数, 则当它左侧全为0, 右侧全为1时, 有最大的01子序列个数, 最大值为0、1个数的乘积,反之为0.

证明:
 区间[a,b]中的排序分别为[o1 l1 o2 l2 ...... on ln],其中oi为第i次出现字符连续为0的串,li是为1
 o = o1 + o2 + ... +on
 l = l1 + l2 + ... +ln
 则有通式: ka = (o1+o2+......+on)*ln + (o1+o2+......+on-1)*ln-1 + ...... + o1*l1
 即 1的长度在0-ln的可能 加上 1的长度在ln-(ln+ln-1)的可能 加上 ...... 加上 1的长度在(l-l1)-l的可能
 显然有 ka<=o*l
 (o1+o2+......+on)*ln + (o1+o2+......+on-1)*ln-1 + ...... + o1*l1<= (o1 + o2 + ... +on)*(l1 + l2 + ... +ln)
 =(o1 + o2 + ... +on)*ln + (o1 + o2 + ... +on)*(ln-1) + ......+ (o1 + o2 + ... +on)*l1
 当且仅当 它左侧全为0, 右侧全为1时, 有最大的01子序列个数, 最大值为0、1个数的乘积,即ka=o*l.
 在根据通式: ka = (o1+o2+......+on)*ln + (o1+o2+......+on-1)*ln-1 + ...... + o1*l1
 易知区间:[a,c]的 k=ka+kb+xa*yb;



 解题:
 对于两个大小区间, D1:[li,ri],D2:[lj,rj], 小区间D1包含于大区间D2, 即lj<=li<=ri<=rj.
 把大区间分成三份:
 [lj,li):长度为n=li-lj, 有xa个0、ya个1、ka个01子序列,
 [li,ri]:长度为l=ri-li+1, 有xi个0、yi个1、ki个01子序列,
 (ri,rj]:长度为m=rj-ri, 有xb个0、yb个1、kb个01子序列,
 则有等式: k = xa*yi + xi*yb + xa*yb + ka + kb + ki
 其中,已知: ki, k, xi, yi, 和区间的 li, ri, lj, rj
 和等式 xi+xa+xb, lr-li+1 = xa+ya, li-rj+1 =xb+yb
 我们只需要枚举xa的个数,就能计算出清楚xb,ya,yb.
 k = xa*yi + xi*yb + xa*yb + ka + kb + ki
 而 0 <= ka + kb <= xa*ya + xb*yb
 所以当大区间的k确定时,我们只需要枚举到一个合适的xa,并尽量的把 ka最大,就能算出其中的一个结果。


*/

class Par{
    int l;
    int r;
    int index;

    public Par (int a , int b, int c){
        l = a;
        r = b;
        index = c;
    }

    @Override
    public String toString() {
        return "l:"+l+",r:"+r+",index:"+index;
    }
}
public class HJ119 {

    //给出区间[l , r],零的个数为cnt0,一的个数为r-l+1-cnt0,字串为k的字符串
    static void fill(int l, int r, int cnt0, int k, int[] ans) {
        int cnt1 = r - l + 1 - cnt0;
        for (int i = l; i <= r; i++) {
            if (k >= cnt1) k -= cnt1;
            else {
                ans[i] = 1;
                cnt1--;
            }
        }
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        int[] ans = new int[n+1];
        int[] l = new int[m+1];
        int[] r = new int[m+1];
        int[] a = new int[m+1];
        int[] b = new int[m+1];
        int[] c = new int[m+1];

        ArrayList<Par> list = new ArrayList<>();

        for (int i = 1; i <= m; i++) {
            l[i] = in.nextInt();
            r[i] = in.nextInt();
            a[i] = in.nextInt();
            b[i] = in.nextInt();
            c[i] = in.nextInt();
            list.add(new Par(l[i],r[i],i));
        }

        Collections.sort(list, new Comparator<Par>() {
            @Override
            public int compare(Par o1, Par o2) {
                if (o1.l > o2.l)
                    return -1;
                if(o1.l==o2.l)
                    return o1.r - o2.r;
                return 1;
            }
        });

        for (int i = 1; i < l.length; i++) {
            System.out.println();
            System.out.println(l[i] + "," + r[i] + "," + a[i] + "," + b[i] + "," + c[i]);
            System.out.println(list.get(i-1).toString());
        }

        for (int i = 1; i <= m; i++) {

            int p = list.get(i-1).index;
            int pre;
            if(i==1) pre = 0;
            else pre = list.get(i-2).index;
            int prel = l[pre];
            int prer=r[pre];
            int lenl = l[pre]-l[p];
            int lenr = r[p]-r[pre];
            int basea=a[pre],baseb=b[pre],basec=c[pre];
            if(i==1) {
                lenl = 0;
                lenr = r[p] - l[p] + 1;
                basea = baseb = basec = 0;
            }
            int cnt0 = a[p] - basea;
            Boolean flag = false;
            for (int x = 0; x <= Math.min(cnt0, lenl); x++) {
                int cnt0l = x, cnt0r = cnt0 - x;
                int cnt1l = lenl - cnt0l;
                int cnt1r = lenr - cnt0r;
                // k - ka -kb
                int basek = basec + cnt0l * baseb + cnt1r * basea + cnt0l * cnt1r;
                // ka,ba取最大的值,即xa*ya + xb*yb
                int addonk = cnt0l * cnt1l + cnt0r * cnt1r;
                if (cnt1l < 0 || cnt1r < 0) continue;
                // k = xa*yi + xi*yb + xa*yb + ka + ba + ki 而 0 <= ka+kb <= xa*ya + xb*yb
                if (c[p] > basek + addonk || c[p] < basek) continue;
                flag = true;
                int addon = c[p] - basek;
                // 尽量的把 ka最大
                if (addon <= cnt0l * cnt1l) {
                    fill(l[p], l[p] + lenl - 1, cnt0l, addon, ans);
                    fill(r[p] - lenr + 1, r[p], cnt0r, 0, ans);
                } else {
                    fill(l[p], l[p] + lenl - 1, cnt0l, cnt0l * cnt1l, ans);
                    fill(r[p] - lenr + 1, r[p], cnt0r, addon - cnt0l * cnt1l, ans);
                }
                break;
            }
            if(!flag){
                System.out.println(-1);
                return;
            }
        }

        for(int i=1;i<=n;i++) System.out.print(ans[i]);

    }

}


发表于 2025-06-26 23:17:28 回复(0)
这一题写的我都抑郁了,测试用例第二个就给100000长度的,测都没法测
发表于 2025-06-10 16:46:51 回复(0)
子序列为从原字符串中删除任意个(可以为零、可以为全部)字符得到的新字符串。
0011有几个子序列?根据示例,应该有且仅有4个,但是:
  • 0011:不删除,但是这个算“新”字符串吗?
  • 011:删除第一个0
  • 11:删除前两个0
  • 1:删除前三位
  • 001:删除最后一个1
  • 00:删除最后两个1
  • 0:删除后三位
  • 01:删除首位两位
是我理解不了还是描述不到位啊?
发表于 2025-05-12 16:41:41 回复(0)
ls1 = list(map(int,input().split()))
n = ls1[0]
m = ls1[1]

def is_right(condi,s):
    l = condi[0]
    r = condi[1]
    x = condi[2]
    y = condi[3]
    k = condi[4]
    in_s = s[l-1:r]
    c_0 = in_s.count('0')
    c_1 = in_s.count('1')
    cnt = 0
    for i in range(len(in_s)-1):
        for j in range(i+1,len(in_s)):
            if in_s[i] == '0' and in_s[j] == '1':
                cnt += 1
    if c_0 == x and c_1 == y and cnt ==k:
        return True
    else:
        return False
condition = []
for i in range(m):
    ls = list(map(int,input().split()))
    condition.append(ls)

b_str = ''.join('1' for _ in range(n))
de = int(b_str,2)
for i in range(de+1):
    s_b = bin(i)[2:].zfill(n)
    k1 = is_right(condition[0],s_b)
    k2 = is_right(condition[1],s_b)
    if k1 and k2:
        print(s_b)
        break

   

发表于 2025-04-20 00:34:30 回复(0)
package main

import (
	"bufio"
	"fmt"
	"math"
	"os"
	"sort"
	"strconv"
	"strings"
)

// 定义区间结构体
type Interval01 struct {
	l, r, x, y, k int
}

// 生成满足条件的字符串
func makeStr(x, k, l int) string {
	y := l - x
	if y == 0 {
		return strings.Repeat("0", l)
	}

	if k%y == 0 {
		return strings.Repeat("0", k/y) + strings.Repeat("1", y) + strings.Repeat("0", x-k/y)
	}

	return strings.Repeat("0", k/y) +
		strings.Repeat("1", y-k%y) +
		"0" +
		strings.Repeat("1", k%y) +
		strings.Repeat("0", x-k/y-1)
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 读取输入
	line, _ := reader.ReadString('\n')
	parts := strings.Fields(line)
	length, _ := strconv.Atoi(parts[0])
	num, _ := strconv.Atoi(parts[1])

	// 读取限制条件
	limits := make([]Interval01, num)
	for i := 0; i < num; i++ {
		line, _ = reader.ReadString('\n')
		parts = strings.Fields(line)
		limits[i].l, _ = strconv.Atoi(parts[0])
		limits[i].r, _ = strconv.Atoi(parts[1])
		limits[i].x, _ = strconv.Atoi(parts[2])
		limits[i].y, _ = strconv.Atoi(parts[3])
		limits[i].k, _ = strconv.Atoi(parts[4])
	}

	// 按区间大小排序
	sort.Slice(limits, func(i, j int) bool {
		return (limits[i].r - limits[i].l) < (limits[j].r - limits[j].l)
	})

	// 处理第一个区间
	li, ri, xi, yi, ki := limits[0].l, limits[0].r, limits[0].x, limits[0].y, limits[0].k

	if ki > xi*yi {
		fmt.Println(-1)
		return
	}

	s := makeStr(xi, ki, ri-li+1)

	// 处理剩余区间
	for idx := 1; idx < num; idx++ {
		lj, rj, xj, yj, kj := limits[idx].l, limits[idx].r, limits[idx].x, limits[idx].y, limits[idx].k
		n, m := li-lj, rj-ri

		// 计算xa的范围
		L := getMax(0, xj-xi-m)
		R := getMin(n, xj-xi)
		if L > R {
			fmt.Println(-1)
			return
		}

		// 计算二次不等式系数
		A := xj + yi + n
		B := kj - ki - xj*(m+xi-xj)
		C := 2*xi + m + yi - xj
		D := kj - ki + xi*(xj-xi-m)

		// 检查判别式
		if A*A < 4*B || C*C < -4*D {
			fmt.Println(-1)
			return
		}

		// 解二次不等式
		left1 := (float64(A) - math.Sqrt(float64(A*A-4*B))) / 2
		right1 := (float64(A) + math.Sqrt(float64(A*A-4*B))) / 2
		left2 := (-math.Sqrt(float64(C*C+4*D)) - float64(C)) / 2
		right2 := (math.Sqrt(float64(C*C+4*D)) - float64(C)) / 2

		// 求交集
		left0 := int(math.Ceil(math.Max(left1, left2)))
		right0 := int(math.Floor(math.Min(right1, right2)))

		if left0 > right0 {
			fmt.Println(-1)
			return
		}

		// 求特解
		var xa int
		if left0 < L {
			if right0 >= L {
				xa = L
			} else {
				fmt.Println(-1)
				return
			}
		} else if left0 <= R {
			xa = left0
		} else {
			fmt.Println(-1)
			return
		}

		// 计算ka和kb
		ka := getMin(D-C*xa-xa*xa, xa*(n-xa))
		kb := getMax(0, D-C*xa-n*xa)
		xb := xj - xi - xa

		// 更新字符串
		s = makeStr(xa, ka, n) + s + makeStr(xb, kb, m)
		li, ri, xi, yi, ki = lj, rj, xj, yj, kj
	}

	// 补充前后缀
	if li > 1 {
		s = strings.Repeat("0", li-1) + s
	}
	if ri < length {
		s = s + strings.Repeat("0", length-ri)
	}

	fmt.Println(s)
}

// 辅助函数
func getMax(a, b int) int {
	if a > b {
		return a
	}
	return b
}

func getMin(a, b int) int {
	if a < b {
		return a
	}
	return b
}

发表于 2025-04-10 14:29:39 回复(0)
"""
核心关系式:
若两个相邻区间:[a,b](有xa个0,ya个1,ka个01子序列), [b+1,c](有xb个0,yb个1,kb个01子序列)
则合成的新区间:[a,c]有ka+kb+xa*yb个01子序列, 即新区间的01子序列个数与原来两个区间具体的排列方式无关.
若已知一个区间的0个数和1个数, 则当它左侧全为0, 右侧全为1时, 有最大的01子序列个数, 最大值为0、1个数的乘积.

对于两个大小区间, D1:[li,ri],D2:[lj,rj], 小区间D1包含于大区间D2, 即lj<=li<=ri<=rj.
把大区间分成三份:
[lj,li):长度为n=li-lj, 有xa个0、ya个1、ka个01子序列,
[li,ri]:长度为l=ri-li+1, 有xi个0、yi个1、ki个01子序列,
(ri,rj]:长度为m=rj-ri, 有xb个0、yb个1、kb个01子序列,
则有等式:
xa + xb + xi = xj       (1)
xa*yi + xi*yb + xa*yb + ka + kb + ki = kj   (2)
xa + ya = n     (3)
xb + yb = m     (4)
不等式:
ka >= 0         (5)
kb >= 0         (6)
xa >= 0         (7)
xb >= 0         (8)
ya >= 0         (9)
yb >= 0         (10)
ka <= xa*ya       (11)
kb <= xb*yb       (12)

剩下就是纯数学计算了, 我的思路是通过消元得到关于xa的不等式组, 查看xa的解的区间是否有整数解.
联立等式(1)(2)(3)(4), 可得:
ka + kb = D - xa*xa - C*xa  (13), 其中C=2*xi+m+yi-xj, D=kj-ki+xi*(xj-xi-m)
再结合(11)+(12):ka + kb <= xa*ya+xb*yb, 和(5)+(6):ka + kb >= 0,
可得两个不等式:
xa*xa - A*xa + B <= 0, 其中 A=xj+yi+n, B=kj-ki-xj*(m+xi-xj)
xa*xa + C*xa - D <= 0,
分别解得:[left1,right1], [left2,right2].
求交集得:xa∈[left0,right0]
联立(3)(7)(9), 可得:
0<=xa<=n
联立(1)(4)(8)(10), 可得:
xj-xi-m<=xa<=xj-xi
求交集得:xa∈[L,R]
再对xa∈[L,R]和xa∈[left0,right0]求交集,即可得xa的解.
求出xa的一个解后, 可根据(3)(6)(11)(13)将ka取最大值, kb取最小值.
"""
from math import sqrt

def makestr(x,k,l):
    '''求满足长度为l, 含x个0, k个01子序列的一个字符串.这个字符串含有y=l-x个1.
    思路:
    1、先构造一个含y*(k//y)个01子序列的字符串,这个字符串左侧全是k//y个0,右侧全是y个1.
    2、在上述字符串中, 在从右往左数第k%y个1的左侧插入一个0, 得到一个含k个01子序列的新字符串.
    3、将剩余的0加入新字符串的最右侧, 所含的01子序列个数仍为k.'''
    y = l-x
    if y==0:
        return "0"*l
    if k%y == 0:
        return "0"*(k//y)+"1"*y+"0"*(x-k//y)
    return "0"*(k//y)+"1"*(y-k%y)+"0"+"1"*(k%y)+"0"*(x-k//y-1)

def main():
    length,num = map(int,input().split())
    limit = [] # 限制条件的集合
    for i in range(num):
        limit.append(list(map(int,input().split())))
    limit.sort(key=lambda x:x[1]-x[0]) # 将限制条件的区间从小到大排序
    li,ri,xi,yi,ki = limit[0]

    if ki > xi*yi: # 检测初始最小区间的限制条件
        return -1
    else: # 创建初始最小字符串
        s = makestr(xi,ki,ri-li+1)

    for idx in range(1,num): # 从小到大依次遍历条件的区间, 逐步扩大已知字符串的范围
        lj,rj,xj,yj,kj = limit[idx] # 大区间及其限制条件
        n,m = li-lj,rj-ri
        L,R = max(0,xj-xi-m),min(n,xj-xi) # xa∈[L,R], 即满足xa,xb,ya,yb非负的xa取值区间
        if L>R:
            return -1

        # 计算二次不等式组系数A,B,C,D
        A = xj+yi+n
        B = kj-ki-xj*(m+xi-xj)
        C = 2*xi+m+yi-xj
        D = kj-ki+xi*(xj-xi-m)

        if A*A<4*B or C*C<-4*D: # 二次不等式判别式, 有解的条件
            return -1

        # 解二次不等式组
        left1 = (A-sqrt(A*A-4*B))/2
        right1 = (A+sqrt(A*A-4*B))/2
        left2 = (-sqrt(C*C+4*D)-C)/2
        right2 = (sqrt(C*C+4*D)-C)/2

        # 求xa∈[left1,right1], x∈[left2,right2]的交集并将边界取整, xa∈[left0,right0]
        left0 = int(-((-max(left1,left2))//1))
        right0 = int(min(right1, right2)//1)
        if left0 > right0:
            return -1

        # 求满足xa∈[left0,right0], xa∈[L,R]的一个特解
        if left0 < L:
            if right0 >= L:
                xa = L
            else:
                return -1
        elif left0 <= R:
            xa = left0
        else:
            print(left0,right0,L,R)
            return -1

        # ka<=D-C*xa-xa*xa, ka<=xa*(n-xa), ka取最大, kb取最小
        ka,kb,xb = min(D-C*xa-xa*xa,xa*(n-xa)),max(0,D-C*xa-n*xa),xj-xi-xa
        s = makestr(xa,ka,n)+s+makestr(xb,kb,m) # 将已知字符串的范围从[li,ri]朝两侧扩展至[lj,rj]
        li,ri,xi,yi,ki = lj,rj,xj,yj,kj # 将当前限制条件更新为小区间的限制条件

    # 最终字符串的长度不足时两边补0
    if li>1:
        s = "0"*(li-1)+s
    if ri < length:
        s = s+"0"*(length-ri)
    return s

r = main() # 时间复杂度,空间复杂度均为O(n+m)
print(r)

发表于 2025-03-17 22:09:52 回复(0)