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

    一、题目

    点此看题

    当你 ( t Wa) 了十几发之后,评测机都会嘲笑你,( t wdnmd),以后还是要写注释以免写错关键细节:

    二、解法

    直接考虑怎么建网络流模型,但是这题是两个点决定一个点(两个 (0) "匹配"中间一个权值),这个关系不太好建。考虑拆分,首先观察到我们按 (0) 数量平均分成两段 (L,R),那么显然不会出现两段内部匹配一个权值,否则我们可以通过调整使之跨过中线,并且这样更优。

    那么可以转成一个点决定一个点,也就是我们只和 (L,R) 中的点匹配,然后和 (0) 的个数除 (2)(min) 即可。设 (l_x) 为颜色 (x) 出现在 (L) 最右端的位置,(r_x) 为颜色 (x) 出现在 (R) 最左端的位置:

    可以手算最小割,发现可能被割的边只可能是 源点与权值的边 和 零点与汇点的边。可以套路地枚举一些东西,我们枚举左边断掉的零点前缀,维护所有右边断掉的零点后缀的答案

    考虑对于对于一种权值,如果当前左边和汇点不连通,那么考虑右边的一段前缀就不需要割掉这个权值了,所以可以拿一棵线段树来维护,只需要区间修改和全局查询,时间复杂度 (O(nlog n))

    三、总结

    网络流中我们常常表示单点,单点对单点的关系,而难以简单地表达多点之间的一种关系。虽然有时候可以利用图上的其他意义(比如路径),但如果要求简单建图,我们这时候可以考虑拆分成单点和单点之间的关系。

    手算最小割常常和贪心法、枚举法、简单数据结构挂钩,很多题的计算方法都类似。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 500005;
    const int inf = 0x3f3f3f3f;
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int T,n,m,k,a[M],ty[M],pz[M],lc[M],rc[M];
    int nxt[M],b[M],mi[4*M],fl[4*M];
    void down(int i)
    {
    	if(!fl[i]) return ;
    	fl[i<<1]+=fl[i];mi[i<<1]+=fl[i];
    	fl[i<<1|1]+=fl[i];mi[i<<1|1]+=fl[i];
    	fl[i]=0;
    }
    void build(int i,int l,int r)
    {
    	fl[i]=0;
    	if(l==r) {mi[i]=b[l];return ;}
    	int mid=(l+r)>>1;
    	build(i<<1,l,mid);
    	build(i<<1|1,mid+1,r);
    	mi[i]=min(mi[i<<1],mi[i<<1|1]);
    }
    void add(int i,int l,int r,int L,int R)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R) {mi[i]--;fl[i]--;return ;}
    	int mid=(l+r)>>1;down(i);
    	add(i<<1,l,mid,L,R);
    	add(i<<1|1,mid+1,r,L,R);
    	mi[i]=min(mi[i<<1],mi[i<<1|1]);
    }
    void work()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		lc[i]=rc[i]=-1,b[i]=inf;
    	for(int i=1;i<=n;i++)
    		a[i]=read(),pz[i]=pz[i-1]+!a[i];
    	m=pz[n];k=0;nxt[n+1]=n+1;
    	for(int i=1;i<=n;i++)
    	{
    		ty[i]=pz[i]<=(m/2)?1:2;
    		if(a[i] && ty[i]==1) lc[a[i]]=i;
    	}
    	for(int i=n;i>=1;i--)
    	{
    		if(a[i] && ty[i]==2) rc[a[i]]=i;
    		if(!a[i]) nxt[i]=i;else nxt[i]=nxt[i+1];
    		//find the next zero-cut
    	}
    	for(int i=1;i<=n;i++)
    		if(lc[i]!=-1 || rc[i]!=-1) k++;
    	for(int i=1;i<=n;i++)
    		if(!a[i] && ty[i]==2) b[i]=m-pz[i]+1+k;
    	b[n+1]=k;build(1,1,n+1);
    	for(int i=1;i<=n;i++)//for every color!!!!
    		if(lc[i]==-1 && rc[i]!=-1)
    			add(1,1,n+1,1,nxt[rc[i]]);
    	int ans=min(m>>1,mi[1]);
    	for(int nw=0,i=1;i<=n;i++)
    	{
    		if(!a[i]) nw++;
    		if(a[i] && lc[a[i]]==i)
    		{
    			if(rc[a[i]]==-1) add(1,1,n+1,1,n+1);
    			else add(1,1,n+1,1,nxt[rc[a[i]]]);
    		}
    		ans=min(ans,nw+mi[1]);
    	}
    	printf("%d
    ",ans);
    }
    signed main()
    {
    	T=read();
    	while(T--) work();
    }
    
  • 相关阅读:
    java实现定时任务(Quartz)
    java实现发送邮件工具
    mysql服务器查询慢原因分析方法
    Vue 插槽
    Vue组件参数传递问题
    Vue路由
    Spring Boot2 拦截器对静态资源的放行
    Spring Boot过滤非法请求
    U盘安装linux(CentOS Kali ubuntu)无法挂载_实测
    如何编译Java程序以及运行程序
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15510650.html
Copyright © 2011-2022 走看看