Educational Codeforces Round 73(DEF)

题目链接

D Make The Fence Great Again(DP)

题意:

n n 个数字,每次操作可以将一个数字加 1 1 ,并且需要代价 v a l i val_i ,问要使得相邻的数字不同最少需要的代价。

思路:

因为只是要求相邻的数字不同,那么每一段相同的数字只要间隔给数字加 1 1 ,在段与段之间最多再加上 1 1 ,所以每一个数字最多加两次,那么令 d p [ i ] [ 0 / 1 / 2 ] dp[i][0/1/2] 表示使前 i i 个数字满足题意,并且第 i i 个数字加 0 / 1 / 2 0/1/2 次的最小费用。就只要判断数字是否相同,转移一下就行。

代码:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=3e5+10;
int n,q;
ll arr[N],w[N];
ll dp[N][4];
int main()
{
	scanf("%d",&q);
	while(q--){
		scanf("%d",&n);
		for(int i=1;i<=n;i++)dp[i][0]=dp[i][1]=dp[i][2]=1e18;
		for(int i=1;i<=n;i++)scanf("%lld%lld",&arr[i],&w[i]);
		dp[1][0]=0,dp[1][1]=w[1],dp[1][2]=w[1]*2;
		for(int i=2;i<=n;i++){
			for(int j=0;j<=2;j++){
				if(arr[i-1]+j!=arr[i])dp[i][0]=min(dp[i][0],dp[i-1][j]);
			}
			for(int j=0;j<=2;j++){
				if(arr[i-1]+j!=arr[i]+1)dp[i][1]=min(dp[i][1],dp[i-1][j]+w[i]);
			}
			for(int j=0;j<=2;j++){
				if(arr[i-1]+j!=arr[i]+2)dp[i][2]=min(dp[i][2],dp[i-1][j]+2*w[i]);
			}
		}
		ll ans=1e18;
		for(int i=0;i<=2;i++)ans=min(ans,dp[n][i]);
		cout<<ans<<endl;
	}
	return 0;
}

E Game With String (思维)

题意:

给定一个字符串 S S ,只包含 . . X X ,两个人轮流操作,先手每次必须选择连续的 a a . . ,把它变成 X X ,后手则每次要选择b个 . . ,若不能操作则输,问谁胜。

思路:

预先处理所有的连续 . . 的长度 l e n i len_i 由于每次只能取固定的长度,而且 b < a b<a 所以 1. 1. 若存在 a > l e n i > = b a>len_i>=b 后手必胜, 2. 2. 就是存在两个以上的 l e n i > = 2 b len_i>=2*b 后手肯定可以将一个 l e n i len_i 变成 1 1 的情况。 3. 3. 如果不存在 l e n i > = 2 b len_i>=2*b 那么 就判断奇偶性即可。 4. 4. 存在一个 l e n i > 2 b len_i>2*b 那么将其进行拆分分类讨论即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a,b;
int T;
char s[N];
int main()
{
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&a,&b);
		scanf("%s",s+1);
		int gs1=0,gs2=0,gs3=0,len;
		int l=strlen(s+1);
		s[l+1]='3';
		int temp=0;
		for(int i=1;i<=l+1;i++){
			//cout<<s[i];
			if(s[i]=='.')temp++;
			else{
				if(temp>=b&&temp<a)gs1++;
				if(temp>=a&&temp<2*b)gs2++;
				if(temp>=2*b)gs3++,len=temp;
				temp=0;
			}
		}
		//puts("");
		//cout<<gs1<<" "<<gs2<<" "<<gs3<<endl;
		if(gs3>=2||gs1){puts("NO");continue;}
		else if(gs3==0){
			if(gs2%2==0)puts("NO");
			else puts("YES");
			continue;
		}
		else if(gs2%2==1){
			if(len>=2*a&&len<=a+3*b-2)puts("YES");
			else puts("NO");
			continue;
		}
		else {
			if(len<=a+2*b-2||(len>=3*a&&len<=a+4*b-2))puts("YES");
			else puts("NO");
			continue;
		}
	}
}

F Choose a Square(线段树)

题意:

在一个平面上有很多点,每一个点有一个权值 v i v_i ,有正有负,要求一个边长为 a a 的正方形并且对角线在 y = x y=x 上,收益为在正方形内的 v i a \sum{v_i}-a ,求出收益的最大值,和对应的正方形坐标。

思路:

首先由于正方形的对角线必须在 y = x y=x 上,所以我们可以沿着这条线枚举正方形的右上方的定点 ( i , i ) (i,i) ,找出使得收益最大的左下方顶点 ( j , j ) (j,j) 在这里插入图片描述
我们可以用 S [ i ] S[i] 表示从 ( 0 , 0 ) (0,0) ( i , i ) (i,i) v i v_i 之和, S 1 [ j ] S1[j] 表示从 ( 0 , 0 ) (0,0) ( j , i ) (j,i) 之和, S 2 [ j ] S2[j] 表示从 ( 0 , 0 ) (0,0) ( i , j ) (i,j) 之和。
那么从 ( j , j ) (j,j) ( i , i ) (i,i) 总收益为 S [ i ] S 1 [ j 1 ] S 2 [ j 1 ] + S [ j 1 ] ( i j ) S[i]-S1[j-1]-S2[j-1]+S[j-1]-(i-j) 可以进行移项变成 S [ i ] i ( S 1 [ j 1 ] + S 2 [ j 1 ] S [ j 1 ] j ) S[i]-i-(S1[j-1]+S2[j-1]-S[j-1]-j) 其中括号中的都是以 j j 为下标那么考虑用线段树维护其最小值,从而使得总收益最大,就是沿着直线 y = x y=x 不断更新区间最小值即可,注意每一个点产生影响应该是其坐标的最小值,还要离散化,细节较多。

代码:

代码细节参考这里不过他维护的是最大值

#include <bits/stdc++.h>
#define ll long long
#define lc x<<1
#define rc x<<1|1
using namespace std;
const int N=1e6+10;
int ls[N*4],rs[N*4];
struct node{
	int pos;
	ll f,mi;
	node(int a=0,ll b=0,ll c=0){pos=a,f=b,mi=c;}
}e[N*4];
node operator + (const node a,const node b){//这个操作挺好用
	node c;c.mi=min(a.mi,b.mi);
	if(a.mi<=b.mi)c.pos=a.pos;
	else c.pos=b.pos;
	return c;
}
void built(int x,int l,int r){
	ls[x]=l;rs[x]=r;
	if(l==r){
		e[x].pos=l;e[x].f=e[x].mi=0;
		return ;
	}
	int mid=(l+r)/2;
	built(lc,l,mid);built(rc,mid+1,r);
	e[x]=e[lc]+e[rc];
}
void down(int x){
	if(e[x].f==0)return ;
	ll f=e[x].f;e[x].f=0;
	e[lc].f+=f;e[rc].f+=f;
	e[lc].mi+=f;e[rc].mi+=f;
	return ;
}
void add(int x,int LL,int RR,ll val){
	if(ls[x]>=LL&&rs[x]<=RR){
		e[x].mi+=val;e[x].f+=val;
		return ;
	}
	down(x);
	int mid=(ls[x]+rs[x])/2;
	if(LL<=mid)add(lc,LL,RR,val);
	if(RR>mid)add(rc,LL,RR,val);
	e[x]=e[lc]+e[rc];
}
node query(int x,int LL,int RR){
	if(ls[x]>=LL&&rs[x]<=RR){
		return e[x];
	}
	down(x);
	int mid=(ls[x]+rs[x])/2;
	if(LL>mid)return query(rc,LL,RR);
	else if(RR<=mid)return query(lc,LL,RR);
	else return query(lc,LL,RR)+query(rc,LL,RR);
}
vector<ll>v;
int getid(int x){return lower_bound(v.begin(),v.end(),x)-v.begin()+1;}
vector<int>G[N];
int n;
int x[N],y[N],m,c[N];
int main()
{
	scanf("%d",&n);
	int x1=1,x2=1;
	for(int i=1,a,b;i<=n;i++){
		scanf("%d%d%d",&a,&b,&c[i]);
		v.push_back(a);v.push_back(b);
		x[i]=a;y[i]=b;
	}
	sort(v.begin(),v.end());v.erase(unique(v.begin(),v.end()),v.end());m=v.size();
	for(int i=1;i<=n;i++){
		x[i]=getid(x[i]),y[i]=getid(y[i]);
		G[max(x[i],y[i])].push_back(i);
	}
	ll sum=0,ans=-1e18;
	built(1,1,m);
	for(int i=1;i<=m;i++){
		for(int pos:G[i]){
			sum+=c[pos];
			add(1,min(x[pos],y[pos]),m,c[pos]);//取最小值做为起点
		}
		if(i!=1){
			node temp=query(1,1,i-1);
			if(ans<=sum-v[i-1]-temp.mi){
				ans=sum-v[i-1]-temp.mi;
				x1=v[temp.pos];
				x2=v[i-1];
			}
		}
		if(sum-(v[i-1]-v[0])>ans){//wa15,因为坐标轴的贡献在后面加入
			ans=sum-(v[i-1]-v[0]);
			x1=v[0];
			x2=v[i-1];
		}
		add(1,i,i,-v[i]);
	}
	if(ans<0){//不加判断小于0会wa
		for(int i=1;i<=5e5+1;i++){
			int k=getid(i);
			if(v[k-1]!=i){
				x1=i,x2=i;break;
			}
		}
		ans=0;
	}
	cout<<ans<<endl;
	cout<<x1<<" "<<x1<<" "<<x2<<" "<<x2<<endl;
}
/*
10
10 0 -1
1 10 -4
3 6 3
4 2 -5
10 7 -1
3 7 3
3 7 -2
8 10 4
5 0 -1
2 3 3


3
8 8 10 10
*/

猜你喜欢

转载自blog.csdn.net/qq_40400202/article/details/101099092