Codeforces Round #703 (Div. 2)

比賽地址node

A(水題)

題目連接
c++

題目:
給出\(n\)堆磚塊,容許將第\(i\)堆磚塊向\(i+1\)堆移動任意塊,則詢問所給磚塊序列能否構成一個單調遞增的序列數組

解析:
所給操做容許將前\(i\)個磚塊以任意形式擺放,因此只須要當前磚塊總數知足遞增序列的最小要求便可spa

#include<bits/stdc++.h>

int main() {
	int T;
	int n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		long long now = 0, cnt = 0, re = 0;
		int t;
		bool ok = true;
		while (n--) {
			scanf("%d", &t);
			now += t;
			if (!ok) continue;
			if (re > now)
				ok = false;
			re += ++cnt;
		}
		printf("%s\n", ok ? "YES" : "NO");
	}
}

B(水題)

題目連接
code

題目:
給出一組點的座標,求出知足曼哈頓距離最小的點的個數排序

解析:
儘管是二維座標,其實能夠與一維座標問題的求解是同樣得,先將數組排序,考慮到某點到任意兩點的距離爲\(d=|x_1-x|+|x_2-x|\),在兩點之間取得最小值,因此能夠每次向兩端取點,這樣最後最小值就落在了中位數
若是是奇數則只有1種取法,偶數則爲中位數的距離,乘法原理處理二維問題便可get

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
long long x[maxn], y[maxn];

int main() {
	int T;
	int n;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		for (int i = 0; i < n; ++i)
			scanf("%lld%lld", &x[i], &y[i]);
		if (n & 1) {
			printf("1\n");
			continue;
		}
		sort(x, x + n); sort(y, y + n);
		printf("%lld\n", (x[n / 2] - x[n / 2 - 1] + 1) * (y[n / 2] - y[n / 2 - 1] + 1));
	}
}

C(二分)

題目連接
⭐⭐it

題目:
給出數組長度\(n\), 最多進行不超過20次詢問,每次詢問\(l,r\),給出\([l,r]\)第二大值的座標,求出最大值的位置io

解析:class

  1. 進行一次\([1,n]\)的詢問,能夠求得整個數組第二大值的座標\(p\)
  2. 考慮到比第二大值只有與最大值在同一區間內時纔會返回\(p\),因此進行第二次詢問肯定\(MAX\in[1,p-1]\)或者\([p+1,n]\)
  3. 不斷二分另外一點的位置,詢問\([ans,p]\)或者\([p,ans]\),求得答案

注意:
若是\(p\)出如今1的位置,則不須要進行第二次詢問,\(MAX\in[2,n]\)是確定的,不然會出現[1,1]的詢問錯誤

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
long long x[maxn], y[maxn];

bool ok;

int main() {
	int n;
	scanf("%d", &n);
	int sec;
	printf("? 1 %d\n\n", n);
	fflush(stdout);
	scanf("%d", &sec);
	int mid;
	int t;
	int l, r;
	if (sec != 1) {
		printf("? 1 %d\n\n", sec);
		fflush(stdout);
		scanf("%d", &t);
	}
	if (sec == 1 || sec != t)
	{
		l = sec + 1, r = n;
		while (r != l) {
			mid = l + (r - l) / 2;
			printf("? %d %d\n\n", sec, mid);
			fflush(stdout);
			scanf("%d", &t);
			if (t == sec)
				r = mid;
			else
				l = mid + 1;
		}
	}
	else {
		l = 1, r = sec - 1;
		while (r != l) {
			mid = l + (r - l + 1) / 2;
			printf("? %d %d\n\n", mid, sec);
			fflush(stdout);
			scanf("%d", &t);
			if (t == sec)
				l = mid;
			else
				r = mid - 1;
		}
	}
	printf("! %d", l);
}

D(中位數求解+二分)

題目連接
⭐⭐⭐

題目:
給出\(n,k\),以及一個長度爲\(n\)的數組,求出一個長度至少爲\(k\)的連續子序列值在排序後的中位數最大值

解析:

  1. 能夠二分答案,假定結果必定大於等於\(x\)
  2. \(x\)的斷定能夠將數組全部的數看做兩類,小於\(x\)的取-1,大於等於\(x\)的取1,求取整個數組前綴和,能夠確定前綴和是連續變化的,所以能夠動態維護差值爲k的最大值與最小值,判斷最大值與最小值的差是否大於0便可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int dat[maxn];
int sum[maxn];

int n, k;

bool check(int x) {
	int mn = 0x3f3f3f3f, mx = -mn;
	for (int i = 1; i <= n; ++i) {
		sum[i] = sum[i - 1] + (dat[i] >= x ? 1 : -1);
		if (i >= k) mn = min(sum[i - k], mn), mx = max(sum[i] - mn, mx);
	}
	return mx > 0;
}

int main() {
	scanf("%d%d", &n, &k);
	for (int i = 1; i <= n; ++i)
		scanf("%d", &dat[i]);
	int l = 1, r = n, mid;
	while (l < r) {
		mid = l + (r - l + 1) / 2;
		if (check(mid)) l = mid;
		else r = mid - 1;
	}
	printf("%d", l);
}

E(思惟+dijkstra)

題目連接
⭐⭐⭐⭐

題目:
給出一幅圖,要求一次必須走兩端路,且費用爲兩端路之和的平方,問從\(1\)點出發到各點的最小費用

解析:
因爲必須走兩端路,所以能夠將路分爲兩段,其中前半段能夠從任意點出發得到,後半段必須從中間點出發得到


  • 方法一(400ns)
    維護兩個距離數組\(dis[N][51]\)\(dis2[N]\)\(dis[i][j]\)表明若以\(i\)點爲中間點,前半段權值爲\(j\),對應的最小費用(最小二字可去除,主要是爲了轉移使用),\(dis2[i]\)表明以\(i\)爲終點的最小費用
    \(dijkstra\),假設給出\(node(state,u,pw,cost),e(u,v,w)\),state=0表明剛通過前半段,1表明剛通過後半段

\[前半段:dis[v][w]=min(dis[v][w],dis2[u]+w^2)\\ 後半段:dis2[v]=min(dis2[v],dis[u][pw]+2\times pw\times w+w^2)\\ 值得注意的是,前半段的權值能夠轉移到後半段,不必定要分開寫 \]

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
typedef pair<int, int> P;
vector<P> e[maxn];
int dis[maxn][51], dis2[maxn];
int n, m;

void addedge(int u, int v, int w) {
	e[u].push_back(P(v, w));
}

struct T {
	int st, pre, pw, c;
	bool operator<(const T& a)const {
		return c > a.c;
	}
};

void dij() {
	priority_queue<T> q;
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	memset(dis2, 0x3f3f3f3f, sizeof(dis2));
	dis2[1] = 0;
	q.push(T{ 0,1,0,0 });
	while (!q.empty()) {
		auto t = q.top();
		q.pop();
		if (t.st) {
			if (dis[t.pre][t.pw] < t.c) continue;
			for (auto& i : e[t.pre]) {
				if (dis2[i.first] > t.c + 2 * t.pw * i.second + i.second * i.second) {
					dis2[i.first] = t.c + 2 * t.pw * i.second + i.second * i.second;
					q.push(T{ 0,i.first,0,dis2[i.first] });
				}
			}
		}
		else {
			if (dis2[t.pre] < t.c) continue;
			for (auto& i : e[t.pre]) {
				if (dis[i.first][i.second] > t.c + i.second * i.second) {
					dis[i.first][i.second] = t.c + i.second * i.second;
					q.push(T{ 1,i.first,i.second,dis[i.first][i.second] });
				}
			}
		}
	}
	for (int i = 1; i <= n; ++i)
		if (dis2[i] == 0x3f3f3f3f) printf("-1 ");
		else printf("%d ", dis2[i]);
}

int main() {
	int a, b, c;
	scanf("%d%d", &n, &m);
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		addedge(a, b, c), addedge(b, a, c);
	}
	dij();
}
  • 方法二(2000ns)(哈希)
    構建新點新邊, 每一個點具備兩個屬性\((v,w)\) \(v\)表明本來對應點,\(w\)表明上一條邊的權重,爲0表明終點,非0表明中間點,跑\(dijkstra\)

注意:
開大數組範圍

#include<bits/stdc++.h>
using namespace std;

const int maxn = 6e6 + 5;
typedef pair<int, int> P;
vector<P> e[maxn];
int dis[maxn];
int n, m;

void addedge(int u, int v, int w) {
	e[u * 51].push_back(P(v * 51 + w, 0));
	for (int i = 1; i <= 50; ++i)
		e[u * 51 + i].push_back(P(v * 51, (w + i) * (w + i)));
}

void dij() {
	priority_queue<P, vector<P>, greater<P>> q;
	memset(dis, 0x3f3f3f3f, sizeof(dis));
	dis[0] = 0;
	q.push(P(0, 0));
	while (!q.empty()) {
		auto t = q.top();
		q.pop();
		if (dis[t.second] < t.first) continue;
		for (auto& i : e[t.second]) {
			if (dis[i.first] > dis[t.second] + i.second) {
				dis[i.first] = dis[t.second] + i.second;
				q.push(P(dis[i.first], i.first));
			}
		}
	}
	for (int i = 0; i < n; ++i)
		if (dis[i * 51] == 0x3f3f3f3f) printf("-1 ");
		else printf("%d ", dis[i * 51]);
}

int main() {
	int a, b, c;
	scanf("%d%d", &n, &m);
	while (m--) {
		scanf("%d%d%d", &a, &b, &c);
		--a, --b;
		addedge(a, b, c), addedge(b, a, c);
	}
	dij();
}
相關文章
相關標籤/搜索