首页 > 其他分享 >CodeForces - 1987F1 & CodeForces - 1987F2

CodeForces - 1987F1 & CodeForces - 1987F2

时间:2024-07-12 10:19:47浏览次数:14  
标签:frac 1987F1 mid CodeForces 区间 操作 1987F2 dp

分析

首先显然有 dp 状态 \(g_i\) 表示前 \(i\) 个数,能进行最大的操作次数。
转移有 \(g_i=\max\limits_{j=1}^{i-1} (g_j+\frac{i-j}{2})[2|(i-j)]\)
但这里显然缺少转移条件。
经过基本观察,发现若 \(i\) 操作过,满足条件:

  • \(a_i \equiv i (mod\ 2)\)
  • \(i\) 左侧操作过 \(\frac{i-a_i}{2}\) 次。

第二条十分关键,这启示我们能否操作只关心前缀。
发现缺少的条件即为经过 \(g_j\) 次操作,\([j,i]\) 区间能否被删完,但是怎么求呢?
再进行基本观察,发现一个点对 \((a_i,a_j)\) 能被操作中间必须被删完。
因此自然地设出状态 \(dp_{l,r,x}\) 表示在 \(l\) 前进行 \(x\) 次操作是否能删除区间 \([i,j]\)。
状态转移方程有:

\[dp_{l,r,x}=dp_{l,mid,x}\ \& \ dp_{mid+1,r,x-\frac{mid-l}{2}}\ \&\ (x\ge \frac{i-a_i}{2}) \]

时间复杂度 \(O(n^4)\),但是只枚举偶数区间,常数带个 \(\frac{1}{2}\),可通过 F1。
观察 \(dp\) 数组性质,发现固定区间 \([l,r]\) 后数组呈 \(000...1111...\),考虑维护第一个 \(1\) 的位置。
原来的状态 \(dp_{l,r}\) 改为删除区间 \([l,r]\)前进行的最少操作次数。
状态转移方程有:

\[dp_{l,r}=\min_{mid} max(\frac{i-a_i}{2},dp_{l,mid},dp_{mid+1,r}-\frac{mid-l}{2}) \]

另外的,如果 \([l+1,r-1]\) 区间能被删除,考虑直接套上一对 \((l,r)\),具体间代码
此时 \(g_j\) 能转移的条件变为了 \(g_j \ge dp_{j,i}\)。
时间复杂度 \(O(n^3)\),可通过 F2.

代码

懒得写 F1 的(

void Main(){
	n=rd;
	for(int i=1;i<=n;i++)
		a[i]=rd,g[i]=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			dp[i][j]=INF;
	for(int len=2;len<=n;len+=2){
		for(int l=1;l+len-1<=n;l++){
			if((l-a[l])&1) continue;
			if(l<a[l]) continue;
			int r=l+len-1;
			if(len==2||dp[l+1][r-1]<=(l-a[l])/2) dp[l][r]=(l-a[l])/2;
			for(int mid=l+1;mid<r;mid+=2){
				dp[l][r]=min(dp[l][r],max((l-a[l])/2,max(dp[l][mid],dp[mid+1][r]-(mid-l+1)/2)));
			}
		}
	}
	for(int i=1;i<=n;i++){
		g[i]=g[i-1];
		for(int j=i-1;j>=1;j-=2)
			if(g[j-1]>=dp[j][i])
				g[i]=max(g[i],g[j-1]+(i-j+1)/2);
	}
	cout<<g[n]<<endl;
}

标签:frac,1987F1,mid,CodeForces,区间,操作,1987F2,dp
From: https://www.cnblogs.com/smilemask/p/18297078

相关文章

  • Codeforces Round 957 (Div. 3)
    推荐个C++编程仓库模板https://github.com/yxc-s/programming-templateA.OnlyPluses总结:为什么优先增加最小的数,它们的乘积会是最优的呢?可以这么理解,假如只有两个数a和b,b>a,那么a+1,就增加一份b。如果b+1,只能增加1份a。因为b>a,所以增加小的数是最优的。voidsolve(){......
  • Codeforces Round #956 (Div. 2)题解
    A.ArrayDivisibility需要让满足$k\midj$的所有\(a_j\)的和整除k,只需要让每个\(a_j\)整除k就可以了,可以让\(a_j=j\)#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineendl'\n'typedefpair<int,int>pii;typedefunsignedlonglo......
  • CodeForces - 1986G1 & CodeForces - 1986G2
    经过基本观察,可得当点对\((i,j)\)合法时,有\(a_i|b_j,a_j|b_i\),其中\(a_i=i/gcd(p_i,i),b_i=p_i/gcd(p_i,i)\),证明显然。如何维护?考虑开\(mp_{x,y}\)表示\(x=a_i\),\(y|b_i\)的个数。对于点\(i\)点对个数即为\(\sum\limits_{d|b_i}mp_{d,a_i}\)时间复杂度为\(O(nlog......
  • CodeForces - 1984E
    题目大意每次在每个联通块中选一个点\(u\),删除这个点使得联通块分成若干个联通块,再从联通块中选点\(v\),在新树上连接\(u,v\),求新树叶节点的最大个数。分析易转化为求原树的最大独立集,设\(f_{u,0/1}\)为以1为根时不选/选\(u\)的最大独立集。显然有:\[dp_{u,0}=\sum\li......
  • 高考后第一次Codeforces Round 952 (Div. 4)
    ACreatingWords思路:拿一个容器交换两数值即可#include<bits/stdc++.h>usingnamespacestd;constintN=100001;chara[N],b[N];intmain(){intn;scanf("%d",&n);while(n--){scanf("%s%s",a,b);charjia......
  • Codeforces Round 954(Div. 3)
    CodeforcesRound954(Div.3)目录CodeforcesRound954(Div.3)\(C\).UpdateQueries\(D\).MathematicalProblem\(E\).BeautifulArray方法一:贪心+滑动窗口方法二:DP\(C\).UpdateQueries对索引数组\(ind\)去重排序对字符串\(c\)排序顺序遍历索引数组,将\(s[ind[i]......
  • Codeforces Round 916 (Div. 3)
    A.ProblemsolvingLog签到题,对于给出的字符串,记录每个字母出现的次数,然后遍历一遍,如果对应的字母出现的次数大于它的位次,则说明该题被解出来了,最后输出解题数量即可点击查看代码#include<iostream>#include<cstdio>#include<algorithm>#include<cmath>#include<vector>#......
  • Codeforces Round 956 (Div. 2) 部分题解A~C
    A.ArrayDivisibility题目大意构造长度为n的数组,满足:所有j的aj之和可以被k整除,其中j是k的倍数,k的取值为1~n。思路构造序列1->n即可满足条件。代码实现voidsolve(){  lln;cin>>n;  for(inti=1;i<=n;i++)cout<<i<<"";  cout<<"\n"......
  • Codeforces Round #956 (Div. 2) and ByteRace 2024
    Preface连着好几天因为熬夜看LOL比赛导致白天精神萎靡,都没精力VP了而且明天就要开始统一训练了,趁着最后一天补一下前两天因为看比赛没打的这场吧这场只能说是战术正确,想了会E没啥思路就马上转头去把F写了,后面回头慢慢想E也想出来了,最后极限2h14min出了EA.ArrayDivisibility......
  • codeforces 955 div 2 D
    题目链接D.Beautyofthemountains题目大意解题思路首先记录所有雪山和没有雪山两种山峰的高度差为\(tot\),然后对于每个可能的子矩,我们可以每次给所有山峰都加一或者减一,因此只要计算出矩阵内两种山峰的个数差的绝对值我们就能得到每次操作该子矩阵对tot的贡献\(z_{i}......