Codeforces Round #703 (Div. 2) A~E题解

本场链接:Codeforces Round #703 (Div. 2)

闲话

感觉CF就没打的好的日子,总是差那么一点就做完了,蛮破坏心情的.CD都是差一点就拐过弯来了.先把A~D写了,E还在施工,下午可能还有事估计得丢晚上再更新E.

A. Shifting Stacks

(n)堆书,每堆书的高度是(h_i).你可以从(i)位置抽若干本书堆到(i+1)列上.操作次数不限,问你能不能把所有书的高度顺次做成一个严格上升序列.

数据范围:

(1 leq n leq 100)

思路

数据范围很小,而且限制比较强:你只能从(i)移动到(i+1).所以不妨这样暴力地把所有的书往后面推:第一摞书如果不是(0)就把所有书全部挪到第二堆上,第二堆书在不少于第一堆书的前提下全部往第三堆书上挪,看她最后是不是一个严格上升序列就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 105;
ll a[N];

int main() 
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n;scanf("%d",&n);
		forn(i,1,n)	scanf("%lld",&a[i]);
		bool ok = 1;
		if(a[1] != 0)
		{
			a[2] += a[1];
			a[1] = 0;
		}
		forn(i,2,n)
		{
			if(a[i] <= a[i - 1])	ok = 0;
			a[i + 1] += a[i] - a[i - 1] - 1;
			a[i] = a[i - 1] + 1;
		}
		if(!ok)	puts("NO");
		else puts("YES");
	}
	return 0;
}

B. Eastern Exhibition

把城市地图看做是一个二维平面,有(n)个居民住在((x_i,y_i))位置上.现在要建立一个博物馆,要求任何人到博物馆的距离(dist = |x-tx| + |y - ty|)相同,其中((tx,ty))表示博物馆的位置.

数据范围:

(1 leq n leq 1000)

(0 leq x_i,y_i leq 10^9)

思路

二维问题分割成两个一维问题:在(x)轴上有多少个位置选定之后,保证所有人的(x)坐标到此绝对值都相同且最小,这个就是货仓选址那个题的经典结论:取所有(x)的中位数,如果(n)是奇数那么中位数唯一;反之可以选中间两个数之间的任何一个位置.那么由于两个子问题相互独立,直接乘法原理统计方案数就可以了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
typedef pair<int,int> pii;
#define x first
#define y second

const int N = 1005;
int x[N],y[N];
int main() 
{
	int T;scanf("%d",&T);
	while(T--)
	{
		int n;scanf("%d",&n);	
		forn(i,1,n)	scanf("%d%d",&x[i],&y[i]);
		if(n & 1)	puts("1");
		else
		{
			sort(x + 1,x + n + 1);
			sort(y + 1,y + n + 1);
			int cx = x[n / 2 + 1] - x[n / 2],cy = y[n / 2 + 1] - y[n / 2];
			printf("%lld
",(1ll * cx + 1) * (cy + 1));
		}
	}
	return 0;
}

C. Guessing the Greatest

交互

给定一个(n)有一个长度为(n)的数组.每次可以发起一个询问([l,r]),返回在([l,r])之内次大值的下标.至多询问40/20次,要求得出最大值的下标.

数据范围:

(2 leq n leq 10^5)

所有元素不同

思路

询问(20)次,很近(log_2^n).考虑构造一个二分框架:([l,r])区间,以及一个中点(mid),如果发起对([l,mid])的询问,是否能正确的移动指针.

很快会发现直接去搞一个二分没什么信息.但是可以这么先来一次:询问出([1,n])里次大值的位置(p).接着可以想到一个方向:假如([1,p])的询问结果是(p)的话,那么最大值一定在([1,p])这个区间内.不过有个例外情况:(p=1)时是不可能的,需要排除.

如果最大值在([1,p])这个区间内,那么现在的问题可以转换成:找最后一个位置(k)满足([k,p]=p),这个二分就可以了.

反过来如果最大值在([p,n])这个区间内,那么同样可以转换成:找第一个位置(k)满足([p,k]=p),同样跑一个二分.

这里其实还有一种想法:把(p)确定之后每次踢掉另外一半的数组,然后去看剩下那部分数组,构成一个范围更小的子问题.但是不能保证查询次数.这样做二分是肯定的.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
typedef pair<int,int> pii;
#define x first
#define y second

map<pii,int> cache;
int query(int l,int r)
{
	if(l >= r)	return -1;
	pii q = {l,r};
	if(cache.count(q))	return cache[q];
	cout << "? " << l << " " << r << endl;
	int res;cin >> res;
	return res;
}

int main() 
{
	ios::sync_with_stdio(0);cin.tie(0);
	int n;cin >> n;
	int l = 1,r = n;
	cout << "? " << "1 " << n << endl;
	int p;cin >> p;
	if(p > 1 && query(1,p) == p)
	{
		int l = 1,r = p;
		while(l < r)
		{
			int mid = l + r + 1 >> 1;
			if(query(mid,p) == p)	l = mid;
			else r = mid - 1;
		}
		cout << "! " << l << endl;
	}
	else
	{
		int l = p,r = n;
		while(l < r)
		{
			int mid = l + r >> 1;
			if(query(p,mid) == p)	r = mid;
			else l = mid + 1;
		}
		cout << "! " << l << endl;
	}
	return 0;
}

D. Max Median

给定一个长度为(n)的数组(a),求一个长度至少为(k)的连续子段,要求中位数最大值.

在本题中,中位数只取((n+1)/2(↓)).

数据范围:

(1 leq k leq n leq 2 * 10^5)

(1 leq a_i leq n)

思路

首先贪心肯定死,考虑dp.但是dp必须要有一个性质:对于以(i)为末尾的段,中位数最大只在长度固定的段取到.但是着很显然不显示.那么只剩一种做法了:直接枚举所有可能的答案,并且检查答案是否合法.枚举部分可以考虑使用二分加速,也是一个比较套路的手段了:二分是合理的原因在于对于一个合理的答案(x),即中位数至少能是(x)的前提下,如果把(x)变小也是成立的,这个性质具有单调性.

直接构造一个二分就可以了,问题剩下一个如何检查中位数是否至少能是(x).问题可以转换成判定:是否存在一个长度至少是(k)的连续子段,满足(geq x)的数的个数严格大于(<x)的数的个数.那么这个就是一个比较套路的东西了,每次可以构造一个数组(s[i] = a[i] >= x ? 1 : -1).并且做一个前缀和.如果严格大于,那么就说明和严格大于(0).问题就转换成了与(0)的大小关系.形式来说:找一个(igeq k)以及一个(j in [0,i-k]).满足(s[i] - s[j] > 0).那么贪心(s[j])只用前面的最小值,维护前缀最小值就可以判断了.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	

const int N = 2e5+7;
int a[N],s[N],minv[N];
int n,k;

bool check(int x)
{
	forn(i,1,n)	s[i] = s[i - 1] + (a[i] >= x ? 1 : -1);
	forn(i,1,n)	minv[i] = min(minv[i - 1],s[i]);
	forn(i,k,n)	if(s[i] - minv[i - k] > 0)	return 1;
	return 0;
}
int main() 
{
	scanf("%d%d",&n,&k);
	forn(i,1,n)	scanf("%d",&a[i]);
	
	int l = 1,r = n;
	while(l < r)
	{
		int mid = l + r + 1 >> 1;
		if(check(mid))	l = mid;
		else r = mid - 1;
	}
	
	printf("%d",l);
	return 0;
}

E. Paired Payment

给定一个(n)(m)边无向带边权图.每次移动至少移动两条边,例如有(a-b-c)则一次移动是直接从(a->c),产生的代价是((w_{ab} + w_{bc})^2).求(1)到所有点的最短距离,如果不能走到输出-1.

数据范围:

(1 leq n leq 10^5)

(1 leq m leq 2 * 10^5)

(1 leq w_i leq 50)

思路

最暴力的做法就是直接从(u)搜所有点(v),再从(v)搜所有点(z)跑单源最短路.考虑如何优化:对于任何一个点(v)如果以他作为中转点,也就是(u-v-z)这样走,那么从(v)走进来的所有边,可以把所有非最小值的踢掉.因为不是最小值的走到(z)不如用最小值的走过去,对所有的点维护一个入边的最小值就可以了.

这个玩意能跑过去的原因是权值的范围非常小,最多不过引发50次额外的更新.也不知道写点什么,就挺神奇的.

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define forn(i,x,n) for(int i = x;i <= n;++i)	
typedef pair<int,int> pii;
#define x first
#define y second

const int N = 1e5+7,M = 4 * N;
int edge[M],succ[M],ver[N],cost[M],idx;
int dist[N],mn[N];
bool st[N];

void add(int u,int v,int w)
{
	edge[idx] = v;
	cost[idx] = w;
	succ[idx] = ver[u];
	ver[u] = idx++;
}

void dijkstra()
{
	priority_queue<pii,vector<pii>,greater<pii>> pq;
	memset(dist,0x3f,sizeof dist);
	memset(mn,0x3f,sizeof mn);
	pq.push({0,1});dist[1] = 0;
	while(!pq.empty())
	{
		auto _ = pq.top();pq.pop();
		int u = _.y,d = _.x;
		if(st[u])	continue;
		st[u] = 1;
		for(int i = ver[u];~i;i = succ[i])
		{
			int v = edge[i],c1 = cost[i];
			if(mn[v] > c1)
			{
				for(int j = ver[v];~j;j = succ[j])
				{
					int z = edge[j],c2 = cost[j];
					if(dist[z] > d + (c1 + c2) * (c1 + c2))
					{
						dist[z] = d + (c1 + c2) * (c1 + c2);
						pq.push({dist[z],z});
					}
				}
				mn[v] = c1;
			}
		}
	}
}

int main() 
{
	memset(ver,-1,sizeof ver);
	int n,m;scanf("%d%d",&n,&m);
	forn(i,1,m)
	{
		int u,v,w;scanf("%d%d%d",&u,&v,&w);
		add(u,v,w);add(v,u,w);
	}
	
	dijkstra();
	
	forn(i,1,n)	if(dist[i] == 0x3f3f3f3f)	printf("-1 ");else printf("%d ",dist[i]);
	return 0;
}
原文地址:https://www.cnblogs.com/HotPants/p/14415009.html