SDOI2018 榮譽稱號(樹形dp)

WAautomaton發表於2019-02-15

題目連結

題目大意

給定一棵完全二叉樹,要求任意一條不拐彎長度為k+1k+1的鏈(即從某個點不斷往上跳kkparentparent),滿足鏈上所有點的和是mm的倍數。
n107,k10n\le 10^7,k\le 10

題解

考慮兩條相鄰的鏈a0,a2,...,aka_0,a_2,...,a_ka1,a2,...,ak+1a_1,a_2,...,a_{k+1},由於他們的和都是mm的倍數,則顯然有a0ak+1 (mod m)a_0\equiv a_{k+1}~(mod~m)
也就是說我們最後只需要考慮編號<2k+1<2^{k+1}的那些點。但是我們需要預處理g[i][j]g[i][j]表示點i(i<2k+1)i(i<2^{k+1})及所有需要和點ii相等的點,全部改成jj所需要的最小代價。
如果暴力的話,也就是暴力列舉jj再暴力列舉所有點,複雜度是O(nm)O(nm)的。但是我們可以把權值mod mmod~m相同的點一起處理,具體的,令all[i][j]all[i][j]表示點i(i<2k+1)i(i<2^{k+1})及所有需要和點ii相等的點中,權值mod m=jmod~m=j的所有點的單次修改代價之和。
於是我們把問題轉化為了mm個點,暴力列舉mm次,這樣就可以在O(2km2)O(2^km^2)的時間內解決了。當然字首和優化一下可以做到O(2km)O(2^km),但我懶。
然後我們就可以在只有2k+112^{k+1}-1個節點上的樹進行dp了。f[i][j]f[i][j]表示所有葉節點到ii的權值和mod mmod~m均為jj的最小修改代價,暴力列舉當前點上的值轉移即可,複雜度O(2km2)O(2^km^2)
於是整道題的複雜度就是O(n+2km2)O(n+2^km^2)了。

#include <bits/stdc++.h>
namespace IOStream {
	const int MAXR = 1 << 23;
	char _READ_[MAXR], _PRINT_[MAXR];
	int _READ_POS_, _PRINT_POS_, _READ_LEN_;
	inline char readc() {
	#ifndef ONLINE_JUDGE
		return getchar();
	#endif
		if (!_READ_POS_) _READ_LEN_ = fread(_READ_, 1, MAXR, stdin);
		char c = _READ_[_READ_POS_++];
		if (_READ_POS_ == MAXR) _READ_POS_ = 0;
		if (_READ_POS_ > _READ_LEN_) return 0;
		return c;
	}
	template<typename T> inline void read(T &x) {
		x = 0; register int flag = 1, c;
		while (((c = readc()) < '0' || c > '9') && c != '-');
		if (c == '-') flag = -1; else x = c - '0';
		while ((c = readc()) >= '0' && c <= '9') x = x * 10 + c - '0';
		x *= flag;
	}
	template<typename T1, typename ...T2> inline void read(T1 &a, T2 &...x) {
		read(a), read(x...);
	}
	inline int reads(char *s) {
		register int len = 0, c;
		while (isspace(c = readc()) || !c);
		s[len++] = c;
		while (!isspace(c = readc()) && c) s[len++] = c;
		s[len] = 0;
		return len;
	}
	inline void ioflush() {
		fwrite(_PRINT_, 1, _PRINT_POS_, stdout), _PRINT_POS_ = 0;
		fflush(stdout);
	}
	inline void printc(char c) {
		_PRINT_[_PRINT_POS_++] = c;
		if (_PRINT_POS_ == MAXR) ioflush();
	}
	inline void prints(char *s) {
		for (int i = 0; s[i]; i++) printc(s[i]);
	}
	template<typename T> inline void print(T x, char c = '\n') {
		if (x < 0) printc('-'), x = -x;
		if (x) {
			static char sta[20];
			register int tp = 0;
			for (; x; x /= 10) sta[tp++] = x % 10 + '0';
			while (tp > 0) printc(sta[--tp]);
		} else printc('0');
		printc(c);
	}
	template<typename T1, typename ...T2> inline void print(T1 x, T2... y) {
		print(x, ' '), print(y...);
	}
}
using namespace IOStream;
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
#define cls(a) memset(a, 0, sizeof(a))

const int MAXN = 10000005, MAXK = 2050, MAXM = 205;
ll f[MAXK][MAXM], g[MAXK][MAXM], all[MAXK][MAXM];
int bel[MAXN], T, n, m, K, Q;
unsigned int SA, SB, SC; int pp, A, B;
unsigned int rng61(){
    SA ^= SA << 16;
    SA ^= SA >> 5;
    SA ^= SA << 1;
    unsigned int t = SA;
    SA = SB;
    SB = SC;
    SC ^= t ^ SA;
    return SC;
}
void gen(){
	cls(g), cls(all), memset(f, 0x3f, sizeof(f));
	read(n, K, m, pp, SA, SB, SC, A, B);
	Q = (1 << (++K)) - 1;
    for (int i = 1; i <= Q; i++) bel[i] = i;
    for (int i = Q + 1; i <= n; i++) bel[i] = bel[i >> K];
    for (int i = 1; i <= pp; i++) {
    	int a, b; read(a, b);
    	all[bel[i]][a % m] += b;
    }
    for (int i = pp + 1; i <= n; i++){
        int a = rng61() % A + 1;
        int b = rng61() % B + 1;
        all[bel[i]][a % m] += b;
    }
    for (int i = 1; i <= Q; i++) {
    	for (int j = 0; j < m; j++) {
    		for (int k = 0; k <= j; k++) g[i][j] += all[i][k] * (j - k);
    		for (int k = j + 1; k < m; k++) g[i][j] += all[i][k] * (j + m - k);
    	}
    }
}
inline void upd(ll &x, ll y) { x = min(x, y); }
int main() {
	for (read(T); T--;) {
		gen();
		for (int i = Q; i > 0; i--) {
			int ls = i << 1, rs = i << 1 | 1;
			if (ls > Q) for (int j = 0; j < m; j++) f[i][j] = g[i][j];
			else if (rs > Q) {
				for (int j = 0; j < m; j++)
				for (int k = 0; k < m; k++)
					upd(f[i][(j + k) % m], f[ls][k] + g[i][j]);
			} else {
				for (int j = 0; j < m; j++)
				for (int k = 0; k < m; k++)
					upd(f[i][(j + k) % m], f[ls][k] + f[rs][k] + g[i][j]);
			}
		}
		printf("%lld\n", f[1][0]);
	}
	return 0;
}

相關文章