• Codeforces Round #743 (Div. 1)


    C. Paint

    题目描述

    给你一个长度为 (n) 的颜色数组,每次可以选择一个位置修改它的颜色,此时与他相邻的极长连续相同颜色段也会改变颜色,问把所有位置变同色的最小操作次数。

    (nleq 3cdot 10^3)

    解法

    因为每次操作的是一个极长同色连续段,所以可以考虑用区间 (dp)

    考虑暴力操作需要用 长度(-1) 步,但是形如 (aba) 操作中间可以少用一步,所以设 (dp[l][r]) 表示把区间 ([l,r]) 染成同色的最大减少操作次数,最后的答案是 (n-1-dp[1][n])

    转移我们需要以减少操作为导向,所以考虑枚举 (a...a) 这种情况,我们找到所有 (s[i]=s[l](l<ileq r))

    [dp[l][r]leftarrow dp[l+1][i-1]+1+dp[i][r] ]

    为什么是 (dp[i][r]) 呢?注意我们的状态定义中并不涉及最终颜色,但是不难观察到:可以通过最小操作步数使一个区间变成初始时它边界上的颜色,所以这两段就可以合并了。

    还有一种简单的情况是直接继承,不操作:

    [dp[l][r]leftarrow dp[l+1][r] ]

    暴力转移时间复杂度 (O(20n^2))

    总结

    (dp) 状态定义注意 (min,max) 的转化,这道如果不换成 ( t max) 就不好用到相同颜色数量有限的条件,因为 (max) 是以减少步数为导向的,这是一种正难则反的思想。

    //Take me to the top , I am ready for...
    #include <cstdio>
    #include <vector>
    #include <cstring>
    #include <iostream>
    using namespace std;
    const int M = 3005;
    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,a[M],dp[M][M];vector<int> b[M];
    void upd(int &x,int y) {x=max(x,y);}
    void work()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    	{
    		b[i].clear();
    		for(int j=1;j<=n;j++) dp[i][j]=0;
    	}
    	for(int i=1;i<=n;i++)
    	{
    		a[i]=read();
    		b[a[i]].push_back(i);
    	}
    	for(int l=n;l>=1;l--)
    		for(int r=l;r<=n;r++)
    		{
    			dp[l][r]=dp[l+1][r];
    			for(auto x:b[a[l]]) if(l<x && x<=r)
    				upd(dp[l][r],dp[l+1][x-1]+1+dp[x][r]);
    		}
    	printf("%d
    ",n-1-dp[1][n]);
    }
    signed main()
    {
    	T=read();
    	while(T--) work();
    }
    

    D. Bridge Club

    题目描述

    (2^n) 个人,编号为 (0 ightarrow 2^n-1),如果两个人二进制位最多相差 (1) 就可以配对,每个人最多配对一次,每对的得分为两个人的点权之和,问最多配 (k) 对的最大得分。

    (nleq 20,kleq 200)

    解法

    可以发现至多 ((2n-1)(k-1)+1) 条边有用,桶排之后暴力网络流即可。

    总结

    缩小问题规模(只考虑和答案有关的量),寻找等价类,是解决不仅限于匹配问题的重要方法。

    //I was the king under your control~~
    #include <cstdio>
    #include <vector>
    #include <iostream>
    #include <queue>
    using namespace std;
    const int M = 20005;
    const int N = 1100005;
    const int inf = 0x3f3f3f3f;
    #define pii pair<int,int>
    #define mp make_pair
    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 n,m,k,t,cnt,a[N],b[N],c[N];vector<pii> w[N<<1];
    int S,T,tot,ans,f[M],in[M],dis[M],flow[M],pre[M],lst[M];
    struct edge
    {
    	int v,c,f,next;
    }e[N];
    void add(int u,int v,int F,int c)
    {
    	e[++tot]=edge{v,c,F,f[u]},f[u]=tot;
    	e[++tot]=edge{u,-c,0,f[v]},f[v]=tot;
    }
    int bfs()
    {
    	for(int i=0;i<=T;i++) dis[i]=-inf;
    	queue<int> q;q.push(S);in[S]=1;
    	dis[S]=flow[T]=0;flow[S]=inf;
    	while(!q.empty())
    	{
    		int u=q.front();q.pop();in[u]=0;
    		for(int i=f[u];i;i=e[i].next)
    		{
    			int v=e[i].v,c=e[i].c;
    			if(e[i].f && dis[v]<dis[u]+c)
    			{
    				dis[v]=dis[u]+c;
    				pre[v]=u;lst[v]=i;
    				flow[v]=min(flow[u],e[i].f);
    				if(!in[v]) q.push(v),in[v]=1;
    			}
    		}
    	}
    	return flow[T]>0;
    }
    signed main()
    {
    	n=read();k=read();
    	m=1<<n;t=(2*n-1)*(k-1)+1;tot=1;
    	for(int i=0;i<m;i++)
    	{
    		a[i]=read();
    		b[i]=__builtin_popcount(i);
    	}
    	for(int i=0;i<(1<<n);i++) if(b[i]&1)
    		for(int j=0;j<n;j++)
    		{
    			int to=i^(1<<j);
    			w[a[i]+a[to]].push_back(mp(i,to));
    		}
    	for(int i=2000000;i>=0;i--)
    	{
    		for(auto x:w[i])
    		{
    			int u=x.first,v=x.second;
    			if(!c[u]) c[u]=++cnt;
    			if(!c[v]) c[v]=++cnt;
    			add(c[u],c[v],1,i);
    			t--;if(t<0) break;
    		}
    		if(t<0) break;
    	}
    	S=0;T=++cnt;
    	for(int i=0;i<m;i++) if(c[i])
    	{
    		if(b[i]&1) add(S,c[i],1,0);
    		else add(c[i],T,1,0);
    	}
    	while(bfs())
    	{
    		if(dis[T]<=0) break;
    		int zy=T,tmp=min(flow[T],k);
    		ans+=dis[T]*tmp;k-=tmp;
    		if(k==0) break;
    		while(zy!=S)
    		{
    			e[lst[zy]].f-=tmp;
    			e[lst[zy]^1].f+=tmp;
    			zy=pre[zy];
    		}
    	}
    	printf("%d
    ",ans);
    }
    

    F. Stations

    题目描述

    Caught up in confusion . Need a resolution.

    (n) 个塔台排成一排,设第 (i) 个塔台的高度是 (h_i),覆盖范围是 (w_i)(i) 能覆盖 (j) 的充要条件是:

    • (ileq jleq w_i)(forall i<kleq j,h_k<h_i)

    一开始所有塔台的高度都为 (0),覆盖范围都为 (i),有下列两种操作:

    • (op=1),重建操作,把塔台 (x) 的高度重建成当前最高,覆盖范围设置成 (y)
    • (op=2),询问操作,设 (b_i) 表示覆盖它的塔台数量,求 (sum_{lleq ileq r} b_i)

    (n,qleq 2cdot 10^5)

    解法

    这道题,是我自己做出来的[骄傲.jpg]

    由于每次重建都会获得最高的塔台,而且它的有效覆盖范围(y),我们只需要考虑这个塔台对其他塔台有效覆盖范围的影响即可,不难发现是对 ([1,x)) 的塔台对 (x-1)(min)

    既然是取 (min) 操作我们可以考虑势能线段树,所以每个塔台的有效覆盖范围是不难维护的,但是这样难以处理询问,我们不妨再拿一棵线段树维护每个点的被覆盖次数,再更新有效覆盖范围的时候更新它即可

    具体算法:势能线段树在 (mx>x-1>cx) 的时候整体取消一个区间的覆盖即可,时间复杂度 (O(nlog^2 n))

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 800005;
    #define ll long long
    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 n,m,mx[M],cx[M],num[M];ll sum[M],tag[M];
    //segment tree II : support simple addition
    void Down(int i,int l,int r)
    {
    	if(!tag[i]) return ;
    	int mid=(l+r)>>1,c=tag[i];
    	sum[i<<1]+=c*(mid-l+1);tag[i<<1]+=c;
    	sum[i<<1|1]+=c*(r-mid);tag[i<<1|1]+=c;
    	tag[i]=0; 
    }
    void Add(int i,int l,int r,int L,int R,int c)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		sum[i]+=(r-l+1)*c;tag[i]+=c;
    		return ;
    	}
    	int mid=(l+r)>>1;Down(i,l,r);
    	Add(i<<1,l,mid,L,R,c);
    	Add(i<<1|1,mid+1,r,L,R,c);
    	sum[i]=sum[i<<1]+sum[i<<1|1];
    }
    ll Ask(int i,int l,int r,int L,int R)
    {
    	if(l>R || L>r) return 0;
    	if(L<=l && r<=R) return sum[i];
    	int mid=(l+r)>>1;Down(i,l,r);
    	return Ask(i<<1,l,mid,L,R)+
    	Ask(i<<1|1,mid+1,r,L,R);
    }
    //segment tree I : support interval Min
    void down(int i)
    {
    	mx[i<<1]=min(mx[i<<1],mx[i]);
    	mx[i<<1|1]=min(mx[i<<1|1],mx[i]);
    }
    void up(int i)
    {
    	num[i]=0;
    	mx[i]=max(mx[i<<1],mx[i<<1|1]);
    	cx[i]=max(cx[i<<1],cx[i<<1|1]);
    	if(mx[i]==mx[i<<1]) num[i]+=num[i<<1];
    	if(mx[i]==mx[i<<1|1]) num[i]+=num[i<<1|1];
    	if(mx[i]!=mx[i<<1]) cx[i]=max(cx[i],mx[i<<1]);
    	if(mx[i]!=mx[i<<1|1]) cx[i]=max(cx[i],mx[i<<1|1]);
    }
    void ins(int i,int l,int r,int id,int c)
    {
    	if(l==r)
    	{
    		Add(1,1,n,id,mx[i],-1);
    		mx[i]=c;num[i]=1;
    		Add(1,1,n,id,mx[i],1);
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	if(mid>=id) ins(i<<1,l,mid,id,c);
    	else ins(i<<1|1,mid+1,r,id,c);
    	up(i);
    }
    void zxy(int i,int l,int r,int c)
    {
    	if(mx[i]<=c) return ;
    	if(mx[i]>c && c>cx[i])
    	{
    		Add(1,1,n,c+1,mx[i],-num[i]);
    		mx[i]=c;return ;
    	}
    	if(l==r)
    	{
    		if(c<mx[i]) Add(1,1,n,c+1,mx[i],-1);
    		mx[i]=c;return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	zxy(i<<1,l,mid,c);
    	zxy(i<<1|1,mid+1,r,c);
    	up(i);
    }
    void upd(int i,int l,int r,int L,int R,int c)
    {
    	if(L>r || l>R) return ;
    	if(L<=l && r<=R)
    	{
    		zxy(i,l,r,c);
    		return ;
    	}
    	int mid=(l+r)>>1;down(i);
    	upd(i<<1,l,mid,L,R,c);
    	upd(i<<1|1,mid+1,r,L,R,c);
    	up(i);
    }
    signed main()
    {
    	n=read();m=read();
    	for(int i=1;i<=n;i++)
    		ins(1,1,n,i,i);
    	for(int i=1;i<=m;i++)
    	{
    		int op=read(),x=read(),y=read();
    		if(op==1)
    		{
    			ins(1,1,n,x,y);
    			upd(1,1,n,1,x-1,x-1);
    		}
    		else
    			printf("%lld
    ",Ask(1,1,n,x,y));
    	}
    }
    
  • 相关阅读:
    《JavaScript高级程序设计》读书笔记 ---Object 类型
    《JavaScript高级程序设计》读书笔记 ---变量、作用域和内存问题小结
    《JavaScript高级程序设计》读书笔记 ---执行环境及作用域
    《JavaScript高级程序设计》读书笔记 ---基本类型和引用类型的值
    《JavaScript高级程序设计》读书笔记 ---函数
    《JavaScript高级程序设计》读书笔记 ---基本概念小结
    《JavaScript高级程序设计》读书笔记 ---语句
    《JavaScript高级程序设计》读书笔记 ---if语句
    Vue2.0组件间数据传递
    UIButton快速点击,只执行最后一次
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15350456.html
Copyright © 2020-2023  润新知