2021牛客暑期多校训练营2

C、Draw Grids

题目大意

给出 n ∗ m n*m nm​的点矩阵,两名玩家,每次玩家要连接一条边,并且保证图中没有成环的地方。

Solution

考点:树形结构

我们知道我们看成 n ∗ m n*m nm个点构成的一片树林的话,最终就是一人连接一条边,最终构成一棵树。

直接判断节点的奇偶性即可。下面的是 P y t h o n Python Python代码。

n,m=map(int,input().split())
p=n*m-1
if p&1:
    print("YES")
else:
    print('NO')

D、Er Ba Game

题目大意

给出两个 p a i r pair pair的比较逻辑,输出两个数的大小关系。

Solution

考点: i f if if逻辑

这里赛后看见一个写的挺好的加位比较的,让最高的逻辑占 10 10 10进制中的最高位,就可以保证他最大,以此类推。

int calc(int a, int b) {
	if (a > b)	swap(a, b);
	if (a == 2 && b == 8)	return 100000;
	if (a == b)	return 9000 + a;
	return 8000 + (a + b) % 10 * 100 + b;
}

int solve() {
	int a1 = read(), b1 = read(), a2 = read(), b2 = read();
	int res1 = calc(a1, b1), res2 = calc(a2, b2);

	if (res1 == res2)	puts("tie");
	else if (res1 > res2)	puts("first");
	else puts("second");

	return 1;
}

F、Girlfriend

题目大意

给出 A , B , C , D A,B,C,D A,B,C,D四个点的三维坐标,给出两个倍数关系 k 1 k_1 k1 k 2 k_2 k2,你可以确定两个几何体 P 1 P_1 P1 P 2 P_2 P2。问你这两个几何体他们的体积交是多少?

几何体 P 1 P_1 P1​的轨迹方程是设点 P ( x 1 , y 1 ) P(x_1,y_1) P(x1,y1)​在几何体上,以及 d i s ( A , P ) = ( x A − x 1 ) ∗ ( x A − x 1 ) + ( y A − y 1 ) ∗ ( y A − y 1 ) dis(A,P)=\sqrt{(x_A-x_1)*(x_A-x_1)+(y_A-y_1)*(y_A-y_1)} dis(A,P)=(xAx1)(xAx1)+(yAy1)(yAy1) ​。这些公式下 d i s ( A , P ) ≥ k 1 ∗ d i s ( B , P ) dis(A,P)\ge k_1*dis(B,P) dis(A,P)k1dis(B,P)。就是 P 1 P_1 P1的轨迹方程,同理可以得到 P 2 P_2 P2的轨迹方程。

Solution

考点:球体积交

首先我们化简 P 1 P_1 P1的轨迹方程,认清它到底是一个什么形状的。

( x A − x 1 ) ∗ ( x A − x 1 ) + ( y A − y 1 ) ∗ ( y A − y 1 ) − k 1 2 ∗ [ ( x B − x 1 ) ∗ ( x B − x 1 ) + ( y B − y 1 ) ∗ ( y B − y 1 ) ] = 0 \displaystyle(x_A-x_1)*(x_A-x_1)+(y_A-y_1)*(y_A-y_1)-k_1^2*[(x_B-x_1)*(x_B-x_1)+(y_B-y_1)*(y_B-y_1)]=0 (xAx1)(xAx1)+(yAy1)(yAy1)k12[(xBx1)(xBx1)+(yBy1)(yBy1)]=0

化简后我们会得到 ( 1 − k 1 2 ) x 1 2 + b x + c = 0 (1-k_1^2)x_1^2+bx+c=0 (1k12)x12+bx+c=0的轨迹方程,很明显当 k = 1 k=1 k=1​的时候它是一条垂直平分线,其余时候它是一个圆。题目也给出 1 < k 1 , k 2 ≤ 100 1<k_1,k_2\le 100 1<k1,k2100​说明这就是两个球体积交,运用上面的化简式子求出单个球的方程,带入球体积交的板子就可以过了。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
#define debug(x) cout << #x << ":" << x << '\n'
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF64 = 0x3f3f3f3f3f3f3f3f;

struct Node {
	ll val;
	int id;
	bool operator < (const Node& opt) const {
		return val < opt.val;
	}
};
ll n, m;


const ld pi = acos(-1);

const int MAX = 100 + 10;
const int inf = 1e9 + 7;

struct Point {
	ld x, y, z, r;

	void print() {
		cout << x << " " << y << " " << z << " " << r << endl;
	}
};

Point a[2];
Point s;

//两点之间距离
ld dis(Point p, Point q) {
	ld ans = sqrt((p.x - q.x) * (p.x - q.x) + (p.y - q.y) * (p.y - q.y) + (p.z - q.z) * (p.z - q.z));
	return ans;
}

typedef tuple<ld, ld, ld> tup3;

ld calc(ld x1, ld x2, ld k) { // 这里建议手推一下
	return (2 * k * k * x2 - 2 * x1) / (1 - k * k);
}

Point calc(tup3 x1, tup3 x2, ld k) {
	auto [X1, Y1, Z1] = x1;
	auto [X2, Y2, Z2] = x2;
	ld A = calc(X1, X2, k);
	ld B = calc(Y1, Y2, k);
	ld C = calc(Z1, Z2, k);
	ld D = ((X1 * X1 - k * k * X2 * X2) + (Y1 * Y1 - k * k * Y2 * Y2) + (Z1 * Z1 - k * k * Z2 * Z2)) / (1 - k * k);
	return { -A / 2,-B / 2,-C / 2,sqrt((A * A + B * B + C * C - 4 * D) / 4) };
}

int solve() {
	n = 1;
	tup3 A[4];
	for (auto& [x, y, z] : A) {
		cin >> x >> y >> z;
	}
	ld k1, k2;
	cin >> k1 >> k2;
	s = calc(A[0], A[1], k1);
	a[0] = calc(A[2], A[3], k2);
	//a[0].print();
	//s.print();
	ld ans = 0;
	for (int i = 0; i < n; i++) {
		ld d = dis(s, a[i]);
		if (d >= s.r + a[i].r) {
			continue;
		}
		else if (d <= fabs(s.r-a[i].r) ) { // 赛中因为这个板子这里没有fabs导致一直wa...
            ld mini = min(s.r,a[i].r);
			ans += (4.0 / 3) * pi * pow(mini,3);
		}
		else {
			ld co = (s.r * s.r + d * d - a[i].r * a[i].r) / (2.0 * d * s.r);
			ld h = s.r * (1 - co);
			ans += (1.0 / 3) * pi * (3.0 * s.r - h) * h * h;
			co = (a[i].r * a[i].r + d * d - s.r * s.r) / (2.0 * d * a[i].r);
			h = a[i].r * (1 - co);
			ans += (1.0 / 3) * pi * (3.0 * a[i].r - h) * h * h;
		}
	}
	cout << ans << endl;

	return 1;
}

int main() {
	int T = read();	rep(_, 1, T)
	{
		solve();
		//cout << (solve() ? "YES" : "NO") << endl;
	}
	return 0;
}

G、League of Legends

题目大意

给出 n ( 1 ≤ n ≤ 5000 ) n(1\le n\le5000) n(1n5000)​个区间,你要把这些区间分成 k k k​组,分在一起的区间就会有线段交,你要输出最大化分组的线段交之和是多少?值得注意的是当就一个区间时,它的贡献是这个区间长度。

Solution

考点:单调队列优化 d p dp dp

参考题解

我们知道添加区间是添加限制,那么对于完全包裹某个小区间的大区间来说,它只有两种最优分配策略。

  1. 把大区间单独分成一组。
  2. 把大区间和它包裹的小区间分成一组,并不影响小区间构成的答案。

所以我们预处理出全部的大区间放在一个新的数组里面,把小区间也放在另外一个数组里面。

我们就要考虑如何求解小区间构成的最优解。

我们知道这些小区间一定是右端点单调递增,左端点也单调递增的。想象着拿一把尺子从左一直向右移动画出的线段。

那么我们将这些小区间 ( a i , b i ) (a_i,b_i) (ai,bi)按照 p a i r pair pair排序之后,我们考虑 f [ i ] [ j ] f[i][j] f[i][j]代表前 j j j个线段分成 i i i组的最大价值。

我们枚举分割点 k k k​,可以写出这样的转移方程: f [ i ] [ j ] = f [ i − 1 ] [ k ] + max ⁡ ( b [ k + 1 ] − a [ j ] , 0 ) f[i][j]=f[i-1][k]+\max(b[k+1]-a[j],0) f[i][j]=f[i1][k]+max(b[k+1]a[j],0)

我们可以看到去掉 max ⁡ \max max符号之后,再看单次的 i i i中, f [ i ] [ k ] + b [ k + 1 ] , b [ k + 1 ] ≥ a [ j ] f[i][k]+b[k+1],b[k+1]\ge a[j] f[i][k]+b[k+1],b[k+1]a[j]​这个是可以用单调队列维护的。

每次把小于当前 a j a_j aj的队首 b [ k ] b[k] b[k]弹出,计算曾经的最优值保存在队尾即可。

复杂度就是 O ( k ∗ n ) O(k*n) O(kn)了。

const int N = 5000 + 7;
struct Node {
	int l, r;
	bool operator < (const Node& opt) const {
		return l < opt.l || (l == opt.l && r > opt.r);
	}
}p[N], a[N];
int f[N][N], q[N], b[N];

int solve() {
	int n = read(), k = read();
	for (int i = 1; i <= n; ++i)	p[i].l = read(), p[i].r = read();
	sort(p + 1, p + 1 + n);
	int maxr = INF, tot = 0, cntb = 0;
	for (int i = n; i; --i) {
		if (p[i].r >= maxr)	b[++cntb] = p[i].r - p[i].l;
		else {
			maxr = p[i].r;
			a[++tot] = p[i];
		}
	}

	sort(a + 1, a + 1 + tot);
	sort(b + 1, b + 1 + cntb, greater<int>());

	memset(f, -1, sizeof f);
	f[0][0] = 0;
	for (int i = 1; i <= k; ++i) {
		int l = 1, r = 0;
		for (int j = 1; j <= tot; ++j) { // 我要把前j个线段分成一共i组,并且[k+1,j]分成另外一组,那么前面一共i-1组
			if (f[i - 1][j - 1] != -1) {
				while (l <= r && f[i - 1][q[r]] + a[q[r] + 1].r <= f[i - 1][j - 1] + a[j].r)
					--r;
				q[++r] = j - 1;
			}
			while (l <= r && a[q[l] + 1].r <= a[j].l)	++l;
			if (l <= r)	f[i][j] = f[i - 1][q[l]] + a[q[l] + 1].r - a[j].l;
		}
	}

	int ans = 0, t = 0;
	for (int i = 0; i <= k; ++i) {
		t += b[i];
		if (f[k - i][tot] != -1) {
			ans = max(ans, t + f[k - i][tot]);
		}
	}
	print(ans);


	return 1;
}

I、Penguins

题目大意

给出两个 20 ∗ 20 20*20 2020​​的地图,左右障碍物不一定相同,两个主人公一开始位于中轴线的最底部,他们执行的左右操作都是镜像的,上下操作都是同步的,你需要输出最短到达当前列的第一行路线以及走过的地图。

Solution

考点: B F S BFS BFS模拟

大模拟…纯粹考验码力,可以注意输入输出格式。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
#define debug(x) cout << #x << ":" << x << '\n'
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }

const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF64 = 0x3f3f3f3f3f3f3f3f;

const int N = 22;

typedef tuple<int, int, int, int> tup;

tup dir[4] = { make_tuple(1,0,1,0),make_tuple(0,-1,0,1),make_tuple(0,1,0,-1),make_tuple(-1,0,-1,0) };
string u[4] = { "D","L","R","U" };

char s[2][N][N];

queue<pair<tup, string>> q;
set<tup> vis;

tup be = make_tuple(19, 19, 19, 0);
tup ed = make_tuple(0, 19, 0, 0);

string bfs() {
	q.push({ be,"" });
	vis.insert(be);
	while (q.size()) {
		auto [pos, str] = q.front();
		q.pop();
		auto [X1, Y1, X2, Y2] = pos;
		rep(i, 0, 3) {
			auto [dirX1, dirY1, dirX2, dirY2] = dir[i];
			int lx = X1 + dirX1;
			int ly = Y1 + dirY1;
			int rx = X2 + dirX2;
			int ry = Y2 + dirY2;
			if (lx < 0 || lx >= 20 || ly < 0 || ly >= 20 || s[0][lx][ly] == '#') {
				lx = X1;
				ly = Y1;
			}
			if (rx < 0 || rx >= 20 || ry < 0 || ry >= 20 || s[1][rx][ry] == '#') {
				rx = X2;
				ry = Y2;
			}
			tup now = make_tuple(lx, ly, rx, ry);
			if (vis.count(now))	continue;
			vis.insert(now);
			string tmp = str + u[i];
			if (now == ed) {
				return tmp;
			}
			q.push({ now,tmp });
		}
	}
	return "";
}

int solve() {
	string tmp;
	rep(i, 0, 19) {
		scanf("%s", s[0][i]);
		scanf("%s", s[1][i]);
	}
	string ans = bfs();
	cout << ans.size() << endl;
	cout << ans << endl;

	int x = 19, y = 19;
	map<char, pai> mp;
	mp['D'] = { 1,0 };
	mp['L'] = { 0,-1 };
	mp['R'] = { 0,1 };
	mp['U'] = { -1,0 };
	s[0][19][19] = 'A';
	for (auto& ch : ans) {
		auto [X, Y] = mp[ch];
		int dx = x + X, dy = y + Y;
		if (dx < 0 || dx >= 20 || dy < 0 || dy >= 20 || s[0][dx][dy] == '#') {
			dx = x, dy = y;
		}
		x = dx, y = dy;
		s[0][x][y] = 'A';
	}

	x = 19, y = 0;
	mp['D'] = { 1,0 };
	mp['L'] = { 0,1 };
	mp['R'] = { 0,-1 };
	mp['U'] = { -1,0 };
	s[1][19][0] = 'A';
	for (auto& ch : ans) {
		auto [X, Y] = mp[ch];
		int dx = x + X, dy = y + Y;
		if (dx < 0 || dx >= 20 || dy < 0 || dy >= 20 || s[1][dx][dy] == '#') {
			dx = x, dy = y;
		}
		x = dx, y = dy;
		s[1][x][y] = 'A';
	}

	rep(i, 0, 19) {
		printf("%s %s\n", s[0][i], s[1][i]);
	}
	return 1;
}

int main() {
	//int T = read();	rep(_, 1, T)
	{
		solve();
		//cout << (solve() ? "YES" : "NO") << endl;
	}
	return 0;
}

J、Product of GCDs

题目大意

给出一个集合,求集合中大小为 k k k的子集他们全部的 gcd ⁡ \gcd gcd之积,对 P P P取模。

Solution

考点:质因数分解,欧拉函数与欧拉降幂

考虑到要求一些数的 gcd ⁡ \gcd gcd那么我们就应该想到按照质因子求解。

分解完了之后我们计算某个区间 gcd ⁡ = 6 \gcd=6 gcd=6的时候我们就可以分成 2 1 , 3 1 2^1,3^1 21,31分别计算了。

具体的我们枚举每个素数 p r i m e prime prime​,我们可以找到因子至少有 p r i m e i prime^i primei​​的个数,那么组合数 C c n t k C_{cnt}^k Ccntk​就是方案数了,组合数可以用杨辉三角递推一下。

所以我们答案应该是 ∏ i = 1 p r i m e i ≤ n p r i m e i ∗ C c n t k \prod_{i=1}^{prime^i\le n} prime^{i*C_{cnt}^k} i=1primeinprimeiCcntk

我们可以正序的按倍数枚举累加,这样就可以保证 p r i m e 1 prime^1 prime1被计算 1 1 1次, p r i m e 2 prime^2 prime2被计算 2 2 2次,以此类推。

接下来就再用欧拉降幂减小这个幂次的大小,直接套用欧拉函数的公式求解 P P P的欧拉函数即可,再用上快速幂注意要开 _ _ i n t 128 \_\_int128 __int128,乘上答案就行了。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
#define debug(x) cout << #x << ":" << x << '\n'
typedef tuple<int, int> tup;
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	__int128 qpow(__int128 a, __int128 b, ll mod) { __int128 ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF64 = 0x3f3f3f3f3f3f3f3f;

const int N = 1e7 + 7;
struct Node {
	ll val;
	int id;
	bool operator < (const Node& opt) const {
		return val < opt.val;
	}
};
ll n, m, phi, p;
int k;

vector<int> primes;
bool vis[N];

ll f[40007][35], a[80007];

inline ll add(ll x, ll y) {
	if (x + y >= phi) return x + y - phi;
	return x + y;
}
inline ll sub(ll x, ll y) {
	if (x < y) return x - y + phi;
	return x - y;
}

void init() {
	for (int i = 2; i < N; ++i) {
		if (!vis[i])	primes.push_back(i);
		for (auto& it : primes) {
			if (it * i >= N)	break;
			vis[it * i] = 1;
			if (i % it == 0)	break;
		}
	}
}

ll CalcPhi(ll x) {
	ll p = x;
	for (auto& it : primes) {
		if (1ll * it * it > x)	break;
		if (x % it == 0) {
			while (x % it == 0)	x /= it;
			p = p / it * (it - 1);
		}
	}
	if (x > 1)	p = p / x * (x - 1);
	return p;
}

int solve() {
	n = read(), k = read(), p = read();
	phi = CalcPhi(p);

    f[0][0] = 1;
	for (int i = 1; i <= n; ++i) {
		f[i][0] = 1;
		for (int j = min(i, k); j > 0; --j)
			f[i][j] = add(f[i - 1][j - 1], f[i - 1][j]);
	}

    memset(a, 0, sizeof a);
	int maxi = -1, x;
	for (int i = 1; i <= n; ++i) {
		x = read();
		++a[x];
		maxi = max(maxi, x);
	}

	ll ans = 1;
	for (auto& it : primes) {
		if (it > maxi)	break;
		ll sum = 0;
		for (ll j = it; j <= maxi; j *= it) {
			for (int z = j + j; z <= maxi; z += j) {
				a[j] += a[z];
			}
			if (a[j] < k)	break;
            //assert(a[j] <=n);
			sum = add(sum, f[a[j]][k]); // 注意这里 mod 欧拉(P)
		}
		if (sum)	ans = (__int128)ans * qpow(it, sum, p) % p; // 这里是 mod p
	}
	print(ans);

	return 1;
}

int main() {
	init();
	int T = read();	rep(_, 1, T)
	{
		solve();
		//cout << (solve() ? "YES" : "NO") << endl;
	}
	return 0;
}

K、Stack

题目大意

给你 k k k​个位置的 b i b_i bi b i b_i bi代表原序列 a a a​在 [ 1 − i ] [1-i] [1i]用单调递增的单调栈求解的栈中元素个数。现在要你返回是否存在这样的 a a a,如果存在想要你构造出一个合理的来。

Solution

考点:单调栈+思维

单调栈应该比较任意懂,把 a i a_i ai放入后依旧要保持栈中元素从底部到栈顶是单调的。如果当前的栈顶不满足这个性质那么就把它 p o p ( ) pop() pop()掉。

我们首先考虑 − 1 -1 1​​的情况,什么时候出现构造失败,我们把输入的已知位置记为 ( i d i , p i ) (id_i,p_i) (idi,pi)​按 i i i​从小到大排好序。

  1. 出现 p i > i d i p_i>id_i pi>idi​这是一种无解的情况。
  2. 出现 p i − p i − 1 > i d i − i d i − 1 p_{i}-p_{i-1}>id_i-id_{i-1} pipi1>idiidi1,说明中间不足以放下那么多数也是无解。

其余时候都是有解的,我们就要想办法构造出这组解。

既然我们得到了这些区间都可以先前衍生直到 1 1 1​,例如 p i = 3 p_i=3 pi=3​我们就可以断言 p i − 1 = 2 , p i − 2 = 1 , p i − 3 = 1... p_{i-1}=2,p_{i-2}=1,p_{i-3}=1... pi1=2,pi2=1,pi3=1...​直到碰见了前一个 p i ! = 1 p_i!=1 pi!=1​的点,我们确实可以控制中间这些 1 1 1​的出现位置,不一定要聚集在前面,但是很快我们能发现,聚集在前面非常容易构造,只要让后面全部的数小于前面的数那么这些多余的 1 1 1的位置就会被全部挤出去。

具体构造的时候,我们记录下那些位置应该 b i = 1 b_i=1 bi=1,那么我们就用 1 1 1开始从后往前填入,这样我可以保证从前往后遍历到达这个点一定 b i = 1 b_i=1 bi=1,接下来遍历 b i = 2 b_i=2 bi=2的点也是同理,从后往前填入,它一定跟在之前的 b i − 1 = 1 b_{i-1}=1 bi1=1后面一个位置,就算这个位置之前没有出现过,但是也被我们预处理填上去了,这时候放入的数一定比 b i − 1 b_{i-1} bi1更大,并且 b i − 1 = 1 b_{i-1}=1 bi1=1,一定满足 b i = 2 b_i=2 bi=2这个性质,一路这样推下去,其余的没有被预装填的点怎么填都无所谓了因为一旦碰到了我们预处理过的点,前面的一定都会被 p o p ( ) pop() pop()掉。

#include <bits/stdc++.h>
using namespace std;
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
#define all(__vv__) (__vv__).begin(), (__vv__).end()
#define endl "\n"
#define pai pair<int, int>
#define ms(__x__,__val__) memset(__x__, __val__, sizeof(__x__))
#define rep(i, sta, en) for(int i=sta; i<=en; ++i)
#define repp(i, sta, en) for(int i=sta; i>=en; --i)
#define debug(x) cout << #x << ":" << x << '\n'
typedef tuple<int, int> tup;
typedef long long ll; typedef unsigned long long ull; typedef long double ld;
inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }
inline ll gcd(ll x, ll y) { return y ? gcd(y, x % y) : x; }
ll qpow(ll a, ll b) { ll ans = 1;	while (b) { if (b & 1)	ans *= a;		b >>= 1;		a *= a; }	return ans; }	ll qpow(ll a, ll b, ll mod) { ll ans = 1; while (b) { if (b & 1)(ans *= a) %= mod; b >>= 1; (a *= a) %= mod; }return ans % mod; }
const int dir[][2] = { {0,1},{1,0},{0,-1},{-1,0},{1,1},{1,-1},{-1,1},{-1,-1} };
const int MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INF64 = 0x3f3f3f3f3f3f3f3f;

const int N = 1e6 + 7;
struct Node {
	int val, id;
	bool operator < (const Node& opt) const {
		return id < opt.id;
	}
}p[N];
ll n, m;

vector<int> G[N];

int a[N];

int solve() {
	n = read(), m = read();
	rep(i, 1, m) {
		p[i].id = read();
		p[i].val = read();
	}

	sort(p + 1, p + 1 + m);

	rep(i, 1, m) {
		if (p[i].val > p[i].id) {
			puts("-1");
			return 0;
		}
		if (p[i].val - p[i - 1].val > p[i].id - p[i - 1].id) {
			puts("-1");
			return 0;
		}
	}

	rep(i, 1, m) {
		for (int j = p[i].val, k = 0; j > 0; --j, ++k) { // 先前预装填
			if (p[i].id - k == p[i - 1].id)	break;
			G[j].push_back(p[i].id - k);
		}
	}

	int u = 0;

	for (int i = 1; i <= n; ++i) {
		for (int j = G[i].size() - 1; ~j; --j) { // 倒序填入
			a[G[i][j]] = ++u;
		}
	}
	rep(i, 1, n) {
		if (a[i] == 0)    a[i] = ++u; // 随便放了
		print(a[i], " \n"[i == n]);
	}

	return 1;
}

int main() {
	//int T = read();	rep(_, 1, T)
	{
		solve();
		//cout << (solve() ? "YES" : "NO") << endl;
	}
	return 0;
}

L-WeChat Walk

题目大意

给出一张图,一共有 q ≤ 2 ∗ 1 0 5 q\le2*10^5 q2105个时刻,每个时刻只有一个点的权值会增大 x x x。并且保证最终图里面最大的点权值 ≤ 1 0 4 \le10^4 104

一个人某个时刻成为冠军是和他相连的全部点这个时刻权值严格小于当前点,如果某个时刻不满足条件了那么这个人就失去了冠军,输出 1 → n 1\to n 1n所有点成为冠军的时长。

Solution

考点:图论分点

题目关键信息1.每次只有一个点权值改变,2.最大权值 ≤ 1 0 4 \le 10^4 104​。

那么我们按照 n \sqrt{n} n 这个边界对图中的点进行分点,记为 s q r sqr sqr

我们将度数大于 s q r sqr sqr的叫做大点,我们将度数小于等于 s q r sqr sqr的叫做小点。

  1. 如果当前遍历的点他是小点,那么暴力维护他连接的点叫做 v v v,并且维护和 v v v相连的最值情况,以便以后维护 v v v的得冠情况。
  2. 对于大点,我们首先判断他和他相邻的大点,接下来就要去小点中更新小点信息,但是我们又不能暴力的再去枚举小点,那么关注到 ≤ 1 0 4 \le 10^4 104这信息,并且如果当前大点 u u u增加了 w w w,那么在最开始把 a u + = w a_u+=w au+=w之后,可更新的小点区间一定在 ( a u − w , a u ] (a_u-w,a_u] (auw,au]之间,只有曾经在这段区间中的小点才会失去冠军,其余小点冠军情况不变,那么我们就开一个 v e c t o r [ u ] [ k ] vector[u][k] vector[u][k]记录大点 u u u在权值为 k k k的时候连接了哪些得冠的小点,这个信息可以在1中维护。
#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

inline ll read() { ll s = 0, w = 1; char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == '-') w = -1; for (; isdigit(ch); ch = getchar())	s = (s << 1) + (s << 3) + (ch ^ 48); return s * w; }
inline void print(ll x, int op = 10) { if (!x) { putchar('0'); if (op)	putchar(op); return; }	char F[40]; ll tmp = x > 0 ? x : -x;	if (x < 0)putchar('-');	int cnt = 0;	while (tmp > 0) { F[cnt++] = tmp % 10 + '0';		tmp /= 10; }	while (cnt > 0)putchar(F[--cnt]);	if (op)	putchar(op); }

const int N = 2e5 + 7;

int n, m, q;

int a[N], mx[N], du[N], id[N];

vector<int> G[N], BigG[N], SmallG[507][10007]; // G:总图, BigG:每个点连接的大点,SmallG:每个曾经获胜的小点玩家连接的旁边的大点

int ans[N], win[N]; // win[i]表示第i个人上次最近一次夺冠在什么时候

void loser(int i, int k) { // 第i个人第k天不是冠军
	if (win[i])	ans[i] += k - win[i];
	win[i] = 0;
}

int main() {
	n = read(), m = read(), q = read();
	int tot = 0, sqr = sqrt(n);
	for (int i = 1; i <= m; ++i) {
		int u = read(), v = read();
		++du[u], ++du[v];
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for (int i = 1; i <= n; ++i) {
		if (du[i] > sqr)	id[i] = ++tot;
		for (auto& v : G[i])
			if (du[v] > sqr)	BigG[i].push_back(v);
	}
	for (int i = 1; i <= q; ++i) {
		int u = read(), w = read();
		a[u] += w;
		if (du[u] <= sqr) { // u是小点
			bool flag = 1;
			for (auto& v : G[u]) { // 直接暴力维护总图连的全部边
				if (du[v] > sqr)	mx[v] = max(mx[v], a[u]);
				if (a[v] > a[u])	flag = 0;
				else { // a[v] <= a[u] 说明v一定不是冠军了
					if (a[v] == a[u])	flag = 0; // 说明u也不是冠军
					loser(v, i);
				}
			}
			if (flag) {
				for (auto& v : BigG[u]) {
					SmallG[id[v]][a[u]].push_back(u);
				}
				if (!win[u])	win[u] = i;
			}
		}
		else { // u是大点
			bool flag = 1;
			for (auto& v : BigG[u]) { // 遍历连接的大点
				if (a[v] > a[u]) flag = 0;
				else { // a[v] <= a[u] 说明v一定不是冠军了
					if (a[v] == a[u])	flag = 0; // 说明u也不是冠军
					loser(v, i);
				}
			}
			for (int k = a[u] - w + 1; k <= a[u]; ++k) { // 只有[a[u]-w+1,a[u]]这个区间内才可能失去冠军
				for (auto& v : SmallG[id[u]][k])
					if (a[v] == k)	loser(v, i);
			}
			//  大点       小点
			if (flag && mx[u] < a[u] && !win[u])	win[u] = i;
		}
	}
	for (int i = 1; i <= n; ++i)	loser(i, q);
	for (int i = 1; i <= n; ++i)	print(ans[i]);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值