1. 程式人生 > 實用技巧 >NOIP第二階段總結

NOIP第二階段總結

聯賽模擬測試37

階段排名 27

A 簡單題

  • 考場上想出a+b+c是定值且q沒啥用,但還是沒找到規律

  • 觀察式子,將A和B加起來就可以把P消掉

  • 然後每次變化就是A+B和C兩個數中大的減小的,小的變兩倍

  • 如果C小,C變兩倍,

  • 如果C大,\(C=C-(A+B)=C-((A+B+C)-C)=2C-(A+B+C)\),C變兩倍減去和,和是定值,答案就轉換成\(C*2^k\mod (A+B+C)\)

Show Code
#include <cstdio>

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar())
        x = x * 10 + c - '0';
    return x * f;
}

int Pow(int a, int k, int M, int ans = 1) {
    for (; k; k >>= 1, a = 1LL * a * a % M)
        if (k & 1) ans = 1LL * ans * a % M;
    return ans;
}

int main() {
    freopen("easy.in", "r", stdin);
    freopen("easy.out", "w", stdout);
    int T = read();
    while (T--) {
        int x = read() + read(), y = read(), k = read();
        x += y;
        printf("%lld\n", 1LL * y * Pow(2, k, x) % x);
    }
    return 0;
}

B 鬥地主

  • 我記得學長講過,但忘了怎麼寫了,本來想寫60分二分圖,然後懶得寫,就寫了個40分暴搜,有個地方寫掛了就只剩20了

  • 每張牌上正面的數向背面的數建雙向邊,一個聯通塊如果是樹就會有一個點不能選,那麼一個區間同時包含這個聯通塊的最大值和最小值,就不能組成

  • 可以維護一個ans[i]表示從i開始最大能到幾,雙指標就可以O(n)預處理出這個陣列,每次查詢就可以做到O(1)了

Show Code
#include <cstdio>

const int N = 2e5 + 5;

int read(int x = 0, int f = 1, char c = getchar()) {
    for (; c < '0' || c > '9'; c = getchar())
        if (c == '-') f = -1;
    for (; c >='0' && c <='9'; c = getchar())
        x = x * 10 + c - '0';
    return x * f;
}

int n, m, f[N], s[N], b[N], ans[N];

int Find(int x) {
    return x == f[x] ? x : (f[x] = Find(f[x]));
}

int main() {
    freopen("playingcard.in", "r", stdin);
    freopen("playingcard.out", "w", stdout);
    n = read(); m = read();
    for (int i = 1; i <= n; ++i) f[i] = i;
    while (m--) {
        int x = Find(read()), y = Find(read());
        s[x]++;
        if (x == y) continue;
        f[y] = x; s[x] += s[y];
    }
    for (int i = 1, j = 1; i <= n; ++i) {
        for (int x; j <= n && b[x=Find(j)] < s[x]; ++j) b[x]++;
        ans[i] = j - 1; b[Find(i)]--;
    }
    m = read();
    while (m--) {
        int x = read();
        puts(read() <= ans[x] ? "Yes" : "No");
    }
    return 0;
}

C 變化的樹

  • 對於x子樹上的點y,它增加的值就是w-k(dep[y]-dep[x]) = w+kdep[x]-kdep[y],

  • 對於每個點維護w+kdep[x]的和sw[i]和k的和sk[i],查詢的時候就是sw[x]-dep[x]sk[x]

  • 那就拿一個支援區間加,單點查的資料結構,其實樹狀陣列就可以,然而我考場寫的大常數線段樹

Show Code
#include <cstdio>

const int N = 3e5 + 5, M = 1e9 + 7;

int read(int x = 0, int f = 1, char c = getchar()) {
	for (; c < '0' || c > '9'; c = getchar())
		if (c == '-') f = -1;
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return x * f;
}

struct Edge {
	int next, t;
}e[N];
int head[N], edc;

void Add(int x, int y) {
	e[++edc] = (Edge) {head[x], y};
	head[x] = edc;
}

int n, dep[N], dfc, dfn[N], a[N], siz[N], tw[N], tk[N];

void Dfs(int x, int fa) {
	siz[x] = 1;
	dep[x] = dep[fa] + 1;
	dfn[x] = ++dfc; a[dfc] = x;
	for (int i = head[x]; i; i = e[i].next) {
		int y = e[i].t;
		Dfs(y, x);
		siz[x] += siz[y];
	}
}

void Add(int x, int w, int k) {
	for (; x <= n; x += x & -x) {
		if ((tw[x] += w) >= M) tw[x] -= M;
		if ((tk[x] += k) >= M) tk[x] -= M;
	}
}

int Ask(int x, bool g, int s = 0) {
	for (; x; x -= x & -x)
		if ((s += (g ? tw[x] : tk[x])) >= M) s -= M;
	return s;
}

int main() {
	freopen("change.in", "r", stdin);
	freopen("change.out", "w", stdout);
	n = read();
	for (int i = 2; i <= n; ++i)
		Add(read(), i);
	Dfs(1, 0);
	int m = read();
	while (m--) {
		int od = read(), x = read();
		if (od == 1) {
			int w = read(), k = read(); 
			w = (w + 1LL * dep[x] * k) % M;
			Add(dfn[x], w, k); Add(dfn[x] + siz[x], -w, -k);
		}
		else printf("%lld\n", (Ask(dfn[x], 1) - 1LL * dep[x] * Ask(dfn[x], 0) % M + M) % M);
	}
	return 0;
}

D 鍊金術 (Unaccepted)

  • 神仙題爆0
Show Code