zoukankan      html  css  js  c++  java
  • CF618F Double Knapsack 构造、抽屉原理

    传送门


    首先,选取子集的限制太宽了,子集似乎只能枚举,不是很好做。考虑加强限制条件:将“选取子集”的限制变为“选取子序列”的限制。在接下来的讨论中我们将会知道:将限制控制得更紧,问题也一定会有解。

    现在我们需要求(A,B)的两个子序列,满足两者的和相等。显然可以前缀和,然后就不会做了qwq

    考虑下面的算法:假定(sumlimits_{a in A} a < sumlimits_{b in B} b)(如果相等直接全选),设序列(A)前缀和为(sumA_i),序列(B)前缀和为(sumB_i)

    对于(n+1)(sumA_i),在(sumB)中找到最小的大于等于它的元素(sumB_j),那么一定有(sumB_j - sumA_i in [0,n)),可能的(sumB - sumA)(n)种,但是有(n+1)(i,j)

    根据抽屉原理,一定会存在两组((i_1,j_1)(i_2,j_2)(i_1 > i_2))满足(sumB_{j_1} - sumA_{i_1} = sumB_{j_2} - sumA_{i_2}),即(sumB_{j_1} - sumB_{j_2} = sumA_{i_1} - sumA_{i_2})。这样我们就找到了一组可行解:在(A)中选择(A_x , x in (i_2 , i_1]),在(B)中选择(B_y , y in (j_2 , j_1])

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<ctime>
    #include<cctype>
    #include<algorithm>
    #include<cstring>
    #include<iomanip>
    #include<queue>
    #include<map>
    #include<set>
    #include<bitset>
    #include<vector>
    #include<stack>
    #include<cmath>
    #include<random>
    //This code is written by Itst
    using namespace std;
    
    inline int read(){
    	int a = 0;
    	char c = getchar();
    	bool f = 0;
    	while(!isdigit(c) && c != EOF){
    		if(c == '-')
    			f = 1;
    		c = getchar();
    	}
    	if(c == EOF)
    		exit(0);
    	while(isdigit(c)){
    		a = a * 10 + c - 48;
    		c = getchar();
    	}
    	return f ? -a : a;
    }
    
    #define PII pair < int , int >
    #define st first
    #define nd second
    #define ll long long
    const int MAXN = 1e6 + 7;
    ll A[MAXN] , B[MAXN];
    PII pos[MAXN];
    
    signed main(){
    #ifndef ONLINE_JUDGE
    	freopen("in","r",stdin);
    	//freopen("out","w",stdout);
    #endif
    	int N = read();
    	for(int i = 1 ; i <= N ; ++i)
    		A[i] = A[i - 1] + read();
    	for(int i = 1 ; i <= N ; ++i)
    		B[i] = B[i - 1] + read();
    	bool f = A[N] > B[N];
    	if(f) swap(A , B);
    	fill(pos , pos + N + 1 , PII(-1 , -1));
    	int p = 0;
    	for(int i = 0 ; i <= N ; ++i){
    		while(B[p] < A[i]) ++p;
    		if(pos[B[p] - A[i]] != PII(-1 , -1)){
    			PII t = pos[B[p] - A[i]];
    			if(!f){
    				printf("%d
    " , i - t.first);
    				for(int j = t.first + 1 ; j <= i ; ++j)
    					printf("%d " , j);
    				printf("
    %d
    " , p - t.second);
    				for(int j = t.second + 1 ; j <= p ; ++j)
    					printf("%d " , j);
    			}
    			else{
    				printf("%d
    " , p - t.second);
    				for(int j = t.second + 1 ; j <= p ; ++j)
    					printf("%d " , j);
    				printf("
    %d
    " , i - t.first);
    				for(int j = t.first + 1 ; j <= i ; ++j)
    					printf("%d " , j);
    			}
    			return 0;
    		}
    		else pos[B[p] - A[i]] = PII(i , p);
    	}
    	puts("-1");
    	return 0;
    }
    
  • 相关阅读:
    读取XML类
    服务器每隔一段时间执行一次任务
    关于购物网站的支付接口(.NET)
    CentOS学习
    CentOS6.4 V1.0
    jquery选择器 选择器性能问题
    css选择器总结
    遮罩层,可拖动
    第四次作业结对编程
    C++中常用类型的转换总结
  • 原文地址:https://www.cnblogs.com/Itst/p/10585514.html
Copyright © 2011-2022 走看看