Codeforces Global Round 10 题解 (DEF)

D. Omkar and Bed Wars

大意:n个人站成一个圈,i的右边是i+1,n的右边是1。他们初始有一个朝向(朝左/右)。每次操作可以让一个人转向(左变右或相反)。要求不出现连续三个朝左或连续三个朝右的人,求最小操作数

拿到题容易想到贪心。当时我仔细思考了亿年,觉得写起来麻烦得一批,于是投入dp的怀抱

\(dp[i][i_1][i_2][k_1][k_2]\) 为前 \(i\) 个人的代价最小值,其中第 \(1\) 个人朝向 \(i_1\),第 \(2\) 个人朝向 \(i_2\),第 \(i-1\) 个人朝向 \(k_1\),第 \(i\) 个人朝向 \(k_2\)相信大家能一眼看出状态转移方程我就懒得讲了

复杂度 \(O(16n)\)

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=200010; typedef long long ll; const int inf=~0u>>2; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;} typedef double lf; const lf pi=acos(-1.0); lf readf(){lf x; if(scanf("%lf",&x)==-1)exit(0); return x;} typedef pair<ll,ll> pii; template<typename T> void operator<<(vector<T> &a,T b){a.push_back(b);}
const ll mod=(1?1000000007:998244353); ll mul(ll a,ll b,ll m=mod){return a*b%m;} ll qpow(ll a,ll b,ll m=mod){ll ans=1; for(;b;a=mul(a,a,m),b>>=1)if(b&1)ans=mul(ans,a,m); return ans;}
#define int ll
int a[N]; char s[N];
int dp[N][2][2][2][2];
void Solve(){
	int n=read();
	scanf("%s",s);
	repeat(i,0,n)a[i]=s[i]=='R';
	repeat(i,0,n)
	repeat(i1,0,2)
	repeat(i2,0,2)
	repeat(k1,0,2)
	repeat(k2,0,2)
		dp[i][i1][i2][k1][k2]=inf;
	repeat(i1,0,2)
	repeat(i2,0,2){
		dp[1][i1][i2][i1][i2]=(a[0]!=i1)+(a[1]!=i2);
	}
	repeat(i,2,n)
	repeat(i1,0,2)
	repeat(i2,0,2)
	repeat(k1,0,2)
	repeat(k2,0,2)
	repeat(k3,0,2)
	if(!(k1==k2 && k2==k3)){
		dp[i][i1][i2][k2][k3]=min(dp[i][i1][i2][k2][k3],
			dp[i-1][i1][i2][k1][k2]+(a[i]!=k3));
	}
	ll ans=inf;
	repeat(i1,0,2)
	repeat(i2,0,2)
	repeat(k1,0,2)
	repeat(k2,0,2){
		if(!(k1==k2 && k1==i1))
		if(!(k2==i1 && k2==i2))
			ans=min(ans,dp[n-1][i1][i2][k1][k2]);
	}
	cout<<ans<<endl;
}
signed main(){
	//freopen("data.txt","r",stdin);
	int T=1; T=read();
	while(T--)Solve();
	return 0;
}

E. Omkar and Duck

大意:(交互题)先告诉你 \(n\),让你给出 \(n\times n\) 的矩阵,然后 \(q\) 组询问,每次会给出 矩阵中 从右上到左下的某一最短路径上 的数字之和,输出路径长什么样

这题我写了两遍题解了,全都不满意删掉了,气死,直接上代码

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=30; typedef long long ll; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;}
#define int ll
int n,a[N][N]; //a即要输出的那个矩阵
pair<ll,ll> g[N][N]; //g[i][j]表示如果走到(i,j)这个位置,那么走过的数字之和一定在区间[g[i][j].first,g[i][j].second]内
//g[][]要求在所有左下-右上的斜线上的所有点,其值域两两不相同,并且单增
vector<pair<ll,ll>> ans;
void Solve(){
	int n=read();
	a[0][0]=0;
	g[0][0]={0,0};
	repeat(sum,1,n*2-1){ //遍历斜线
		int pre=-1;
		repeat(j,0,n)
		repeat(i,0,n)
		if(i+j==sum){ //处理斜线上的点
			int mn=INF,mx=-INF;
			if(i)mn=min(mn,g[i-1][j].fi),mx=max(mx,g[i-1][j].se);
			if(j)mn=min(mn,g[i][j-1].fi),mx=max(mx,g[i][j-1].se); //此时,[mn,mx]为前两个点的值域的并集
			g[i][j]={pre+1,pre+1+mx-mn}; //pre+1是为了与刚刚处理的点值域刚好不相交
			a[i][j]=g[i][j].fi-mn;
			pre=pre+1+mx-mn; //值域最大值
		}
	}
	repeat(i,0,n){
		repeat(j,0,n)
			printf("%lld ",a[i][j]);
		puts("");
	}
	fflush(stdout);
	int q=read();
	while(q--){
		ans.clear();
		int s=read(); int x=n-1,y=n-1; ans.push_back({x,y});
		while(x || y){ //模拟一遍,把路径模拟出来
			if(!x)y--;
			else if(!y)x--;
			else{
				s-=a[x][y];
				if(g[x-1][y].fi<=s && g[x-1][y].se>=s)
					x--;
				else y--;
			}
			ans.push_back({x,y});
		}
		repeat_back(i,0,ans.size())
			printf("%lld %lld\n",ans[i].fi+1,ans[i].se+1);
		fflush(stdout);
	}
}
signed main(){
	//freopen("data.txt","r",stdin);
	int T=1; //T=read();
	while(T--)Solve();
	return 0;
}

F. Omkar and Landslide

发生了一个恐怖的事情

我2020年1月的时候随手出了一道题,当时是打算给noip提高组出题,并且锻炼一下出题能力。结果刚好和这题撞了,甚至输入输出格式也是一样的,唯一区别是我的n是2e5,这题是1e6,还有高度我是1e9,这题是1e12(有区别吗?)

(当时觉得,从算法角度看,应该是不到noip提高组的难度的,也就写起来有点繁琐)

芜湖,找来以前的代码直接嘿嘿嘿,老铁们我做得对吗

大意:给出n个严格单调递增的数列,不断重复如下操作直到无法操作:选择一个i满足 a[i+1]-a[i]>=2,然后 a[i+1]--; a[i]++;

憋说了,大模拟,莽就完了。先a[i]-=i,然后模拟一个栈,每个元素表示一段相同的a[i],然后就是各种讨论,好烦啊,还能咋做捏

我回来了。不是吧阿sir,原来这题是我那题的简单版本,因为我的题目的初始序列是单调不减的,这题是单调递增的,那最终状态只与sum(a[i])有关。那为什么放F题位置啊喂!

#include <bits/stdc++.h>
using namespace std;
#define repeat(i,a,b) for(int i=(a),_=(b);i<_;i++)
#define repeat_back(i,a,b) for(int i=(b)-1,_=(a);i>=_;i--)
#define mst(a,x) memset(a,x,sizeof(a))
#define fi first
#define se second
//#define endl "\n"
mt19937 rnd(chrono::high_resolution_clock::now().time_since_epoch().count());
int cansel_sync=(ios::sync_with_stdio(0),cin.tie(0),0);
const int N=1000010; typedef long long ll; const int inf=~0u>>2; const ll INF=~0ull>>2; ll read(){ll x; if(scanf("%lld",&x)==-1)exit(0); return x;} typedef double lf; const lf pi=acos(-1.0); lf readf(){lf x; if(scanf("%lf",&x)==-1)exit(0); return x;} typedef pair<ll,ll> pii; template<typename T> void operator<<(vector<T> &a,T b){a.push_back(b);}
#define int ll
int n,a[N];
vector<pii> s;
void Solve(){
	n=read();
	repeat(i,1,n+1)
		a[i]=read(),a[i]-=i;
	s.push_back({a[1],1});
	repeat(i,2,n+1){
		if(a[i]<s.back().first){
			s.push_back({a[i],i});
			continue;
		}
		if(a[i]==s.back().first)continue;
		while(s.size()>1 && a[i]-s.back().first>i-s.back().second){
			a[i]-=i-s.back().second;
			s.pop_back();
		}
		if(s.size()==1){
			int t=(a[i]-s.back().first)/(i-1+1);
			a[i]-=t*(i-1);
			s.back().first+=t;
		}
		if(a[i]==s.back().first)continue;
		int d=a[i]-s.back().first;
		pii t=s.back();
		if(s.size()==1)s.back().first++;
		else s.pop_back();
		s.push_back({t.first,t.second+d});
	}
	s.push_back({0,INF});
	int p=0;
	repeat(i,1,n+1){
		if(s[p+1].second==i)p++;
		printf("%lld ",s[p].first+i);
	}
	puts("");
}
signed main(){
	//freopen("data.txt","r",stdin);
	int T=1; //T=read();
	while(T--)Solve();
	return 0;
}
posted @ 2020-08-17 01:35  axiomofchoice  阅读(644)  评论(1编辑  收藏  举报