zoukankan      html  css  js  c++  java
  • CF1408H. Rainbow Triples

    题目大意

    题解

    好题

    设0的个数为z,一个显然的结论是答案上界为z/2

    以第z/2个0为分界划开,左边的称为L右边的称为R,那么L中右侧和R中左侧的0个数>=z/2

    可以发现这样转化之后一个点只需要考虑在其所在集合的连边,即L集考虑向左的边R集考虑向右的边

    因为总数<=z/2而任意一边的0个数>=z/2,每个数只会对某一边贡献一次,所以一定有解

    同一种颜色只需要保留L集最右点和R集最左点,然后显然是网络流

    L集中i->i-1,R集中i->i+1,一个颜色向两个集合中的两个点(x,y)连边,S向颜色连边,0向T连边,因为n是5e5所以应该跑不过

    考虑用最小割求最大流,如果一种颜色没有被割那么其向两个集合连边的前缀x和后缀y的0都要被割,所以割掉的是一段前缀0一段后缀0和一些颜色

    那么枚举割掉的前缀长度,把(x,y)按x排序扫描线+线段树维护后缀的答案即可

    code

    #include <bits/stdc++.h>
    #define fo(a,b,c) for (a=b; a<=c; a++)
    #define fd(a,b,c) for (a=b; a>=c; a--)
    #define max(a,b) (a>b?a:b)
    #define min(a,b) (a<b?a:b)
    #define ll long long
    //#define file
    using namespace std;
    
    struct type{int x,y;} b[500001];
    int tr[2000011],Tr[2000011],a[500001],T,n,N,i,j,k,l,I,sum,ans;
    
    void down(int t,int len)
    {
    	if (Tr[t])
    	{
    		if (len>1)
    		Tr[t*2]+=Tr[t],Tr[t*2+1]+=Tr[t];
    		tr[t]+=Tr[t],Tr[t]=0;
    	}
    }
    void up(int t) {tr[t]=min(tr[t*2]+Tr[t*2],tr[t*2+1]+Tr[t*2+1]);}
    void change(int t,int l,int r,int x,int y,int s)
    {
    	int mid=(l+r)/2;
    	if (x<=l && r<=y) {Tr[t]+=s;down(t,r-l+1);return;}
    	
    	if (x<=mid) change(t*2,l,mid,x,y,s);
    	if (mid<y) change(t*2+1,mid+1,r,x,y,s);
    	up(t);
    }
    
    bool cmp(type a,type b) {return a.x<b.x;}
    
    int main()
    {
    	#ifdef file
    	freopen("CF1408H.in","r",stdin);
    	freopen("b.out","w",stdout);
    	#endif
    	
    	scanf("%d",&T);
    	for (;T;--T)
    	{
    		scanf("%d",&n),sum=0,N=n+1;
    		memset(tr,0,(N*4+1)*4);
    		memset(Tr,0,(N*4+1)*4);
    		fo(i,1,n) scanf("%d",&a[i]),sum+=!a[i];
    		
    		if (a[n]==2)
    		n=n;
    		
    		if (sum<2) {printf("0
    ");continue;}
    		l=0;
    		fo(I,1,n) if (!a[I]) {++l;if (l==sum/2) break;}
    		
    		fo(i,1,n) b[i]={0,N};
    		fo(i,1,I) b[a[i]].x=i;
    		fd(i,n,I+1) b[a[i]].y=i;
    		
    		l=0;
    		fd(i,n,1) l+=!a[i],change(1,1,N,i,i,l);
    		Tr[1]+=n,down(1,N);
    		sort(b+1,b+n+1,cmp);
    		ans=2147483647,j=1,l=0;
    		fo(i,1,n)
    		{
    			while (j<=n && i>=b[j].x)
    			change(1,1,N,1,b[j].y,-1),++j;
    			ans=min(ans,l+(tr[1]+Tr[1]));
    			l+=!a[i];
    		}
    		
    		printf("%d
    ",min(ans,sum/2));
    	}
    	
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    python爬虫之MongoDB测试环境安装
    python爬虫之pandas
    python爬虫之正则表达式
    SQL实现递归及存储过程中In()参数传递解决方案[转]
    SET XACT_ABORT 的用法[转]
    SqlServer排序(null值,和非空值排列顺序)
    poi操作excel设置数据有效性
    Oracle:ORA-01791: 不是 SELECTed 表达式
    java poi 合并单元格后边框问题
    java压缩文件出现中文乱码问题
  • 原文地址:https://www.cnblogs.com/gmh77/p/13823304.html
Copyright © 2011-2022 走看看