zoukankan      html  css  js  c++  java
  • [codeforces 618 F] Double Knapsack (抽屉原理)

    题目链接:http://codeforces.com/contest/618/problem/F

    题目:

    题目大意:

    有两个大小为 N 的可重集 A, B, 每个元素都在 1 到 N 之间. 分别找出 A 和 B 的一个子集, 使得这两个子集元素之和相等.

    分别输出集合A和集合B的子集的个数以及子集中元素在原集合中的位置

    N ≤ $10^6$

    题解:

    首先我们证明一个结论,存在一组方案,满足两个子集在A中和在B中都是连续的一段

    把两个集合看成两个数组,分别计算出前缀和sa,sb

    对于每个i(0<=i<=n),我们j为满足0<=sa[i]-sb[j]<=n-1的最大的j,设d[i]=sa[i]-sb[j],可以发现j的单调递增的

    我们发现d数组一共有n+1个元素(包括i=0,sa[i]=0的情况),并且我们又发现d[i]的取值只有n个。那么根据抽屉原理,至少有两个d数组中的元素是相等的

    于是我们有sa[i']-sb[j']=sa[i]-sb[j](i'>i)

    移项之后得到sa[i']-sa[i]=sb[j']-sb[j]

    这个时候我们就知道在A数组中i+1~i'这一段元素之和与B数组中j+1~j'这一段元素之和相等

    证毕

    其实上面的证明过程就是我们本题的做法,我们用two pointers处理出d数组,然后判断当前的d值在之前是否出现过,这个时候我们就得到了答案

    值得注意的是,我们需要sa[n]<=sb[n],因为若是sa[n]>sb[n]可能出现对于i=n找不到一个j满足上述条件

    代码:

    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    
    const int N=1e6+15;
    int n,cnt1,cnt2,st1,st2,ed1,ed2;
    int sa[N],sb[N],vis[N],p[N];
    inline int read()
    {
        char ch=getchar();
        int s=0,f=1;
        while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
        while (ch>='0'&&ch<='9') {s=(s<<3)+(s<<1)+ch-'0';ch=getchar();}
        return s*f;
    }
    void solve(int a[],int b[])
    {
        memset(vis,-1,sizeof(vis));
        int j=0;
        for (int i=0;i<=n;i++)
        {
            while (a[i]-b[j]>=n) j++;
            int d=a[i]-b[j];
            if (vis[d]!=-1)
            {
                cnt1=i-vis[d];
                st1=vis[d]+1;ed1=i;
                cnt2=j-p[vis[d]];
                st2=p[vis[d]]+1;ed2=j;
                break;
            }
            vis[d]=i;p[i]=j;
        }
    }
    int main()
    {
        n=read();
        for (int i=1;i<=n;i++) sa[i]=sa[i-1]+read();
        for (int i=1;i<=n;i++) sb[i]=sb[i-1]+read();
        if (sa[n]<=sb[n])
        {
            solve(sa,sb);
        }
        else 
        {
            solve(sb,sa);swap(cnt1,cnt2);swap(st1,st2);swap(ed1,ed2);    
        }
        printf("%d
    ",cnt1);
        for (int i=st1;i<=ed1;i++) printf("%d ",i);
        printf("
    ");
        printf("%d
    ",cnt2);
        for (int i=st2;i<=ed2;i++) printf("%d ",i);
        printf("
    ");
        return 0;
    }
  • 相关阅读:
    servlet上传图片 服务器路径(转)
    图片和提交servlet的相对和绝对路径
    Intel 的面试经历中国研究院
    CentOS-6.5-x86_64 最小化安装,已安装包的总数,这些包?
    西门子PLC学习笔记8-(计时器)
    这个周末我太累了
    windows7股票的,win8残疾人,安装Han澳大利亚sinoxn个时间,sinox它支持大多数windows软体
    net.sf.json 迄今 时刻 格式 办法
    ar命令提取.a时刻,一个错误 is a fat file (use libtool(1) or lipo(1) and ar(1) on it)
    POJ 2187: Beauty Contest(旋转卡)
  • 原文地址:https://www.cnblogs.com/xxzh/p/9535463.html
Copyright © 2011-2022 走看看