LGR-156-Div.3 题解

发布时间 2023-08-26 23:02:47作者: maniubi

LGR-156-Div.3 题解

洛谷网校 8 月普及组月赛 I & MXOI Round 1 & 飞熊杯 #2

第一次AK一个比赛!而且排名这么靠前!!!

T1 宝箱

题目链接

思路

注意到答案有两种情况。1.从原点走到 \(a\),再从 \(a\) 走到 \(b\) ,2.从原点走到 \(b\),再从 \(b\) 走到 \(a\)。取一个最小值即可。

代码

int myabs(int x) {
	return x < 0 ? -x : x;	
}
int a, b, ans = 0x7fffffff;
int main() {
	read(a), read(b);
	ans = min(ans, myabs(a - 0) + myabs(a - b));
	ans = min(ans, myabs(b - 0) + myabs(b - a));
	write(ans);
	return 0;
}

T2 方格

题目链接

思路

注意到每个点好朋友的个数即与它的数字相同的点的个数减去与它相邻且数字相同的点的个数。又注意到 \(a_{i,j} \le 9\) ,前者用桶维护,后者暴力判断即可,注意实现时需减去自己,答案最大是 \(nm\) 级别的,需开 long long。时间复杂度: \(O(nm)\)

代码

const int MAXN = 2005;
ll n, m, a[MAXN][MAXN];
ll t[20], ans;
int main() {
	read(n), read(m);
	for (int i = 1; i <= n; i ++)
		for (int j = 1; j <= m; j ++) {
			read(a[i][j]);
			t[a[i][j]] ++;
		}
	for (int i = 1; i <= n; i ++) 
		for (int j = 1; j <= m; j ++) {
			ans += t[a[i][j]] - 1;
			if (i > 1 && a[i - 1][j] == a[i][j])
				ans --;
			if (i < n && a[i + 1][j] == a[i][j])
				ans --;
			if (j > 1 && a[i][j - 1] == a[i][j])
				ans --;
			if (j < m && a[i][j + 1] == a[i][j])
				ans --;
		}
	write(ans);
	return 0;
}

T3 涂色

题目链接

思路

注意到答案为涂色后层数 \(\mod k \ne 0\) 的格子个数。又注意到格子的层数为其行涂色的次数加上其列涂色的次数,分别设为 \(a, b\),答案即 \((a+b)\mod k \ne 0\) 的总数。枚举每一行的 \(a\),答案即 \(m - (满足b \mod k = k-a的b的个数)\) ,统计每行每列涂色的次数,用桶维护 \(b \mod k\) ,每次查桶即可,答案最大是 \(nm\) 级别的,需开 long long。时间复杂度:\(O(n)\)

代码

const ll MAXN = 200005, MAXK = 500005;
ll n, q, k, sum, ans;
ll h[MAXN], l[MAXN], t[MAXK];
int main() {
    read(n), read(m), read(q), read(k);
	for (ll i = 1, op, x; i <= q; i ++) {
		read(op), read(x);
		if (op == 1) h[x] ++; // 统计
		else l[x] ++;
	}
	for (ll i = 1; i <= n; i ++) h[i] %= k; // mod
	for (ll i = 1; i <= m; i ++) {
		l[i] %= k; // mod
		t[l[i]] ++;	// 桶
	}
	for (ll i = 1; i <= n; i ++) 
		ans += m - t[(k - h[i]) % k]; // 查桶
	write(ans);
	return 0;
}

T4 城市

题目链接

思路

设点 \(x\) 到其他点的距离之和为 \(sum_x\) ,注意到加入的点对答案的贡献为 \(2sum_{k} + 2nw\),只需预处理出 \(sum_k\) 即可。又注意到这是一个换根 \(dp\) ,令 1 为根节点,先 dfs 一遍求出 \(sum_1\) 以及以点 \(x\) 为根的子树大小 \(siz_x\) ,再 dfs 一遍换根,求出每个 \(sum\) ,设当前点为 \(y\)\(x\)\(y\) 的父亲,连边的权值为 \(z\) ,状态转移方程:

\[sum_y=sum_x - siz_yz+(siz_1-siz_y)z=sum_x-z(siz_1-2siz_y) \]

时间复杂度:\(O(n)\),实现细节看代码。

代码

ll tot, ver[MAXN << 1], nxt[MAXN << 1], head[MAXN], edge[MAXN << 1];
ll n, q, sum[MAXN], siz[MAXN], ans, nowans; // 模数大,不开long long见祖宗
void add(ll x, ll y, ll z) {
	ver[++ tot] = y;
	nxt[tot] = head[x];
	head[x] = tot;
	edge[tot] = z;
}
void dfs1(ll x, ll fa) {
	siz[x] = 1; // 子树大小
	for (ll i = head[x], y; i; i = nxt[i]) {
		y = ver[i];
		if (y == fa) continue;
		dfs1(y, x); 
		siz[x] += siz[y];
		sum[x] = 1ll * (sum[x] + sum[y] + (1ll * edge[i] * siz[y]) % mod) % mod;
	}
}
void dfs2(ll x, ll fa) {
	for (ll i = head[x], y; i; i = nxt[i]) {
		y = ver[i];
		if (y == fa) continue;
		sum[y] = 1ll * (sum[x] + 1ll * (siz[1] - 2ll * siz[y]) % mod * edge[i] % mod) % mod;
        // 转移方程
		sum[y] = (sum[y] % mod + mod); // 注意处理负数情况
		dfs2(y, x);
	}
}
int main() {
    read(n), read(q);
	for (ll i = 1, u, v, c; i < n; i ++) {
		read(u), read(v), read(c);
		add(u, v, c);
		add(v, u, c);
	}  
    dfs1(1, 0); // 两次dfs
    dfs2(1, 0);
    for (ll i = 1; i <= n; i ++) 
    	ans = 1ll * (ans % mod + sum[i] % mod) % mod; // 不加点的答案
    for (ll i = 1, k, w; i <= q; i ++) {
    	read(k), read(w);
    	nowans = 1ll * (ans % mod + 2ll * (sum[k] % mod + n % mod * w % mod) % mod) % mod;
        // 处理加点后的答案
    	write(nowans);
    	putchar('\n');
	}
	return 0;
}