首页 > 试题广场 >

小红的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
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)