Educational Codeforces Round 73(DEF)

題目鏈接

D Make The Fence Great Again(DP)

題意:

nn 個數字,每次操作可以將一個數字加 11 ,並且需要代價 valival_i ,問要使得相鄰的數字不同最少需要的代價。

思路:

因爲只是要求相鄰的數字不同,那麼每一段相同的數字只要間隔給數字加 11 ,在段與段之間最多再加上 11 ,所以每一個數字最多加兩次,那麼令 dp[i][0/1/2]dp[i][0/1/2] 表示使前 ii 個數字滿足題意,並且第 ii 個數字加 0/1/20/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 (思維)

題意:

給定一個字符串 SS ,只包含 ..XX,兩個人輪流操作,先手每次必須選擇連續的 aa.. ,把它變成 XX ,後手則每次要選擇b個 .. ,若不能操作則輸,問誰勝。

思路:

預先處理所有的連續 .. 的長度 lenilen_i 由於每次只能取固定的長度,而且 b<ab<a 所以 1.1. 若存在 a>leni>=ba>len_i>=b 後手必勝, 2.2. 就是存在兩個以上的 leni>=2blen_i>=2*b 後手肯定可以將一個 lenilen_i 變成 11 的情況。 3.3. 如果不存在 leni>=2blen_i>=2*b 那麼 就判斷奇偶性即可。 4.4. 存在一個 leni>2blen_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(線段樹)

題意:

在一個平面上有很多點,每一個點有一個權值 viv_i ,有正有負,要求一個邊長爲 aa 的正方形並且對角線在 y=xy=x 上,收益爲在正方形內的via\sum{v_i}-a,求出收益的最大值,和對應的正方形座標。

思路:

首先由於正方形的對角線必須在 y=xy=x 上,所以我們可以沿着這條線枚舉正方形的右上方的定點 (i,i)(i,i) ,找出使得收益最大的左下方頂點(j,j)(j,j)在這裏插入圖片描述
我們可以用 S[i]S[i] 表示從 (0,0)(0,0)(i,i)(i,i)viv_i 之和, S1[j]S1[j] 表示從 (0,0)(0,0)(j,i)(j,i) 之和, S2[j]S2[j] 表示從 (0,0)(0,0)(i,j)(i,j) 之和。
那麼從 (j,j)(j,j)(i,i)(i,i) 總收益爲 S[i]S1[j1]S2[j1]+S[j1](ij)S[i]-S1[j-1]-S2[j-1]+S[j-1]-(i-j) 可以進行移項變成 S[i]i(S1[j1]+S2[j1]S[j1]j)S[i]-i-(S1[j-1]+S2[j-1]-S[j-1]-j) 其中括號中的都是以 jj 爲下標那麼考慮用線段樹維護其最小值,從而使得總收益最大,就是沿着直線 y=xy=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
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章