十二省联考 2019 简要题解

从这里开始

  这个 day 1 真有毒。一个普及题,一个有点小胖的普及题,一个不知道在干嘛的题。感觉 day 1 质量远不如 csp。

  突然开始想吹 scoi 2019

  感觉现状是,遇到一道简单题:

  • 别人:读题 $\rightarrow$ 哦,傻逼题 $\overset{码码码}{\rightarrow}$ AC
  • 我:读题 $\rightarrow$ 进入憨批模式 $\overset{一段很长的时间}{\rightarrow}$ 好像完成了第一步$\overset{一段很长的时间}{\rightarrow}$ 好像会了  $\overset{码码码}{\rightarrow}$ 随机结果

  flag.省选退役++

  感觉自己省选会完蛋的次数++

Day 1

Problem A 异或粽子

  堆扩展 + 主席树。

  听说是 bzoj 非权限题原题

Code

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

const int N = 5e5 + 5;

#define ll long long

typedef class TrieNode {
	public:
		int cnt;
		TrieNode* ch[2];

		TrieNode() :cnt(0) {}
		TrieNode(TrieNode* self) :cnt(0) {
			ch[0] = ch[1] = self;
		}
} TrieNode;

TrieNode pool[N * 35];
TrieNode* _top = pool;
TrieNode nul (&nul);
#define nul &nul

void insert(TrieNode* po, TrieNode* &p, int dep, ll v) {
	p = _top++;
	*p = *po;
	p->cnt++;
	if (dep < 0)
		return;
	int c = (v >> dep) & 1;
	insert(po->ch[c], p->ch[c], dep - 1, v);
}
ll query(TrieNode* p, ll pr, int dep, int k, ll v) {
	if (dep < 0)
		return pr;
	int c = (v >> dep) & 1;
	int rs = (p->ch[c ^ 1]) ? (p->ch[c ^ 1]->cnt) : 0;
	if (k <= rs)
		return query(p->ch[c ^ 1], pr << 1 | 1, dep - 1, k, v);
	return query(p->ch[c], pr << 1, dep - 1, k - rs, v);
}

typedef class Node {
	public:
		ll v;
		int k, id;

		Node(ll v, int k, int id) : v(v), k(k), id(id) {	}

		boolean operator < (Node b) const {
			return v < b.v;
		}
} Node;

int n, K;
ll a[N];
TrieNode* rts[N];

int main() {
	scanf("%d%d", &n, &K);
	ll sum = 0;
	rts[0] = nul;
	for (int i = 1; i <= n; i++) {
		ll x;
		scanf("%lld", &x);
		insert(rts[i - 1], rts[i], 33, sum);
		a[i] = (sum ^= x);
	}
	priority_queue<Node> Q;
	for (int i = 1; i <= n; i++) {
		Q.push(Node(query(rts[i], 0, 33, 1, a[i]), 1, i));
	}
	ll ans = 0;
	while (K--) {
		Node e = Q.top();
		Q.pop();
		ans += e.v;
		int id = e.id, k = e.k;
		if (k < e.id) {
			Q.push(Node(query(rts[id], 0, 33, k + 1, a[id]), k + 1, id));
		}
	}
	printf("%lld\n", ans);
	return 0;
}

Problem B 字符串问题

  建一个后缀树,然后优化建图跑拓扑序。

  因为是向子树内连边,所以后缀树上每个点建一个虚点,每条边对每种涉及到的长度建虚点,然后就做完了。

Code

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

#define ll long long

const int N = 2e5 + 5;
const int _N2 = N << 1;
const int N6 = N * 6;
const int bzmax = 19;
const int inf = (signed) (~0u >> 2);
const ll llf = (signed ll) (~0ull >> 2);

int cti(char x) {
	return x - 'a';
}

typedef class TrieNode {
    public:
        int len;
        TrieNode* par;
        TrieNode* ch[26];
} TrieNode;
 
TrieNode pool1[N << 1];
TrieNode* _top = pool1;
 
TrieNode* newnode(int len) {
    _top->len = len;
	_top->par = NULL;
	memset(_top->ch, 0, sizeof(_top->ch));
    return _top++;
}
 
typedef class SuffixAutomaton {
    public:
        TrieNode *rt, *lst;
 
		void reset() {
			_top = pool1;
			rt = newnode(0);
			lst = rt;
		}

        TrieNode* extend(char _) {
            int c = cti(_);
            TrieNode* p = newnode(lst->len + 1);
            TrieNode* f = lst;
            while (f && !f->ch[c])
                f->ch[c] = p, f = f->par;
            if (!f) {
                p->par = rt;
            } else {
                TrieNode* q = f->ch[c];
                if (q->len == f->len + 1) {
                    p->par = q;
                } else {
                    TrieNode* nq = newnode(f->len + 1);
                    memcpy(nq->ch, q->ch, sizeof(nq->ch));
                    nq->par = q->par, q->par = p->par = nq;
                    while (f && f->ch[c] == q)
                        f->ch[c] = nq, f = f->par;
                }
            }
            return lst = p;
        }
 
		int build(vector<int>* Tr, int* dep) {
			int m = _top - pool1;
			for (int i = 0; i < m; i++) {
				if (pool1[i].par)
					Tr[pool1[i].par - pool1 + 1].push_back(i + 1);
				dep[i + 1] = pool1[i].len;
			}
			return m;
		}
} SuffixAutomaton;

typedef class Event {
	public:
		int op;
		int l, len;
		int id;

		Event(int op, int l, int r, int id) : op(op), l(l), len(r - l + 1), id(id) {	}

		boolean operator < (Event b) const {
			return (len ^ b.len) ? (len < b.len) : (op > b.op);
		}
} Event;

typedef class Edge {
	public:
		int ed, nx;

		Edge() {	}
		Edge(int ed, int nx) : ed(ed), nx(nx) {	}
} Edge;

typedef class MapManager {
	public:
		vector<int> h;
		vector<Edge> E;

		void init(int n) {
			h.resize(n + 1);
			E.clear();
			fill(h.begin(), h.end(), -1);
		}
		void add_edge(int u, int v) {
			E.emplace_back(v, h[u]);
			h[u] = (signed) E.size() - 1;
		}
		Edge& operator [] (int p) {
			return E[p];
		}
} MapManager;

int T, n, na, nb, m;
int N1, N2, N3;
SuffixAutomaton sam;
int nodes[N];
int dep[_N2], lstp[_N2], lstlen[_N2];
int bz[_N2][bzmax];

ll f[N6];
int deg[N6], v[N6];
vector<int> Tr[_N2];
MapManager G;

void dfs(int p, int fa) {
	bz[p][0] = fa;
	lstlen[p] = dep[fa] + 1;
	for (int i = 1; i < bzmax; i++)
		bz[p][i] = bz[bz[p][i - 1]][i - 1];
	for (auto e : Tr[p])
		if (e ^ fa)
			dfs(e, p);
}

int jump(int p, int len) {
	for (int i = bzmax; i--; ) {
		int u = bz[p][i];
		if (dep[u] >= len) {
			p = u;
		}
	} 
	return p;
}

void add_edge(int u, int v) {
	G.add_edge(u, v);
	deg[v]++;
}

void solve() {
	static char s[N];
	scanf("%s", s + 1);
	n = strlen(s + 1);
	sam.reset();
	for (int i = n; i; i--)
		nodes[i] = sam.extend(s[i]) - pool1 + 1;
	N1 = sam.build(Tr, dep);
	dfs(1, 1);
	for (int i = 1; i <= N1; i++) {
		lstp[i] = i;
	}
	scanf("%d", &na);
	N2 = N1 + na;
	vector<Event> E;
	for (int i = 1, l, r; i <= na; i++) {
		scanf("%d%d", &l, &r);
		E.emplace_back(0, l, r, N1 + i);
		v[N1 + i] = r - l + 1;
	}
	scanf("%d", &nb);
	N3 = N2 + nb;
	for (int i = 1, l, r; i <= nb; i++) {
		scanf("%d%d", &l, &r);
		E.emplace_back(1, l, r, N2 + i);
	}
	G.init(N3 + na + nb);
	sort(E.begin(), E.end());
	for (auto e : E) {
		int l = e.l, len = e.len, p = jump(nodes[l], len), cp = e.id;
		if (len ^ lstlen[p]) {
			add_edge(lstp[p], ++N3);
			lstp[p] = N3;
			lstlen[p] = len;
		}
		p = lstp[p];
		if (!e.op) {
			add_edge(p, cp);
		} else {
			add_edge(cp, p);
		}
	}
	scanf("%d", &m);
	for (int i = 1, u, v; i <= m; i++) {
		scanf("%d%d", &u, &v);
		add_edge(N1 + u, N2 + v);
	}
	for (int p = 1; p <= N1; p++) {
		for (auto q : Tr[p]) {
			add_edge(lstp[p], q);
		}
	}
	fill(f + 1, f + N3 + 1, -llf);
	queue<int> Q;
	for (int i = 1; i <= N3; i++) {
		if (!deg[i]) {
			Q.push(i);
			f[i] = 0;
		}
	}
	while (!Q.empty()) {
		int p = Q.front();
		Q.pop();
		f[p] += v[p];
		for (int i = G.h[p], e = G[i].ed; ~i && (e = G[i].ed, 1); i = G[i].nx) {
			f[e] = max(f[e], f[p]);
			if (!--deg[e]) {
				Q.push(e);
			}
		}
	}
	ll ans = 0;
	for (int i = 1; i <= N3; i++) {
		if (deg[i]) {
			ans = -1;
			break;
		}
		ans = max(ans, f[i]);
	}
	printf("%lld\n", ans);
	fill(deg + 1, deg + N3 + 1, 0);
	fill(v + 1, v + N3 + 1, 0);
	for (int i = 1; i <= N1; i++)
		Tr[i].clear();
}

int main() {
	scanf("%d", &T);
	while (T--)
		solve();
	return 0;
}

Problem C 骗分过样例

  Please stop writing problems.

Day 2

Problem A 皮配

  注意到总人数一定,所以限制只有关于其中一个阵营或者派系的数量的上界和下界限制。

  假设表示阵营变量是 $x$,表示派系的是 $y$。考虑一下每个城市的生成函数大概是 $x^{\sum s_i}\prod_i F_{1, i}(y) + \prod_i F_{0, i}(y)$

  考虑把 $F_{1, i}(y) = F_{0, i}(y) =  (1 + y^{s_i})$ 的式子作为公因式提出来。然后现在总的的式子大概是 $[x^{[l_1, r_1]}][y^{[l_2, r_2]}] \prod _i x^{k_i} G_{i, 1} (y) + G_{i, 0}(y)) \prod_{j} (1 + y^{s_j})$。

  将前后分开计算,然后合并一下背包。

  时间复杂度 $O(T(nM + c^2s_iM))$

Code

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

const int N = 1005;
const int Mod = 998244353;

#define ll long long

typedef class Zi {
  public:
    int v;

    Zi() : v(0) {	}
    Zi(int x) : v(x) {	}
    Zi(ll x) : v(x % Mod) {	}

    friend Zi operator + (Zi a, Zi b) {
      return ((a.v += b.v) >= Mod) ? (a.v -= Mod) : a;
    }
    friend Zi operator - (Zi a, Zi b) {
      return ((a.v -= b.v) < 0) ? (a.v += Mod) : a;
    }
    friend Zi operator * (Zi a, Zi b) {
      return 1ll * a.v * b.v;
    }
} Zi;

typedef vector<Zi> Poly;

int T, n, c, C0, C1, D0, D1, m;

Poly operator + (Poly a, Poly b) {
  if (a.size() < b.size())
    a.resize(b.size());
  for (unsigned i = 0; i < b.size(); i++)
    a[i] = a[i] + b[i];
  return a;
}

vector<int> s;
vector<Zi> f;
vector<int> ban;
vector<vector<Zi>> g, h;
vector<vector<int>> G;

void solve() {
  cin >> n >> c;
  cin >> C0 >> C1 >> D0 >> D1;
  int sum = 0;
  s.resize(n);
  G.resize(c);
  for (int i = 0, b; i < n; i++) {
    cin >> b >> s[i];
    G[--b].push_back(i);
    sum += s[i];
  }
  cin >> m;
  ban.resize(n, -1);
  for (int i = 0, x, y; i < m; i++) {
    cin >> x >> y;
    ban[--x] = y;
  }
  C1 = max(sum - C1, 0);
  D1 = max(sum - D1, 0);
  swap(C0, C1);
  swap(D0, D1);
  if (C0 > C1 || D0 > D1) {
    cout << 0 << '\n';
    return;
  }
  f.resize(D1 + 1, 0);
  f[0] = 1;
  for (int i = 0; i < n; i++) {
    if (ban[i] == -1) {
      for (int j = D1; j >= s[i]; j--) {
        f[j] = f[j] + f[j - s[i]];
      }
    }
  }
  int sumX = 0, sumx;
  Poly tmp;
  vector<int> pos;
  g.resize(1, vector<Zi>(1));
  g[0][0] = 1;
  for (int t = 0; t < c; t++) {
    sumx = 0;
    boolean flag = true;
    for (auto x : G[t]) {
      if (ban[x] != -1) {
        flag = false;
      }
      sumx += s[x];
    }
    if (!flag || !sumx)
      continue;
    sumX = min(sumX + sumx, C1);
    g.resize(sumX + 1);
    for (int i = sumX; i >= sumx; i--)
      g[i] = g[i] + g[i - sumx];
  }
  for (int t = 0; t < c; t++) {
    sumx = 0;
    pos.clear();
    for (auto x : G[t]) {
      if (ban[x] != -1) {
        pos.push_back(x);
      }
      sumx += s[x];
    }
    if (pos.empty())
      continue;
    h.resize(1 + min(sumX + sumx, C1));
    for (int i = 0; i <= sumX && i + sumx <= C1; i++) {
      tmp = g[i];
      for (auto p : pos) {
        if (!(ban[p] & 2)) {
          if (ban[p] == 1) {
            tmp.resize(tmp.size() + s[p]);
            int l = tmp.size();
            for (int j = l - 1; j >= s[p]; j--)
              tmp[j] = tmp[j - s[p]];
            fill(tmp.begin(), tmp.begin() + s[p], Zi(0));
          }
        } else {
          tmp.resize(tmp.size() + s[p]);
          int l = tmp.size();
          for (int j = l - 1; j >= s[p]; j--)
            tmp[j] = tmp[j] + tmp[j - s[p]];
        }
      }
      h[i + sumx] = h[i + sumx] + tmp;
    }
    for (int i = 0; i <= sumX; i++) {
      tmp = g[i];
      for (auto p : pos) {
        if (ban[p] & 2) {
          if (ban[p] == 3) {
            tmp.resize(tmp.size() + s[p]);
            int l = tmp.size();
            for (int j = l - 1; j >= s[p]; j--)
              tmp[j] = tmp[j - s[p]];
            fill(tmp.begin(), tmp.begin() + s[p], Zi(0));
          }
        } else {
          tmp.resize(tmp.size() + s[p]);
          int l = tmp.size();
          for (int j = l - 1; j >= s[p]; j--)
            tmp[j] = tmp[j] + tmp[j - s[p]];
        }
      }
      h[i] = h[i] + tmp;
    }
    sumX = min(sumX + sumx, C1);
    g = h;
    h.clear();
  }
  for (int i = 1; i <= D1; i++)
    f[i] = f[i] + f[i - 1];
  Zi ans = 0;
  for (int i = C0; i <= C1 && i <= sumX; i++) {
    for (int j = 0; j < (signed) g[i].size(); j++) {
      if (!g[i][j].v)
        continue;
      int ql = max(0, D0 - j);
      int qr = D1 - j;
      if (ql <= qr) {
        Zi tmp = f[qr];
        (ql) && (tmp = tmp - f[ql - 1], 0);
        ans = ans + g[i][j] * tmp;
      }
    }
  }
  cout << ans.v << '\n';
}

void clear() {
  s.clear();
  f.clear();
  g.clear();
  G.clear();
  ban.clear();
}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> T;
  while (T--) {
    solve();
    clear();
  }
  return 0;
}

Problem B 春节十二响

  考虑维护子树内的决策。合并两个子树的时候把最大的取 max,次大的取 max,以此内推。

  注意到集合大小总是子树的深度。因此这个过程的复杂度为 $O(n\log n)$。

  正确性我还不太会证明,但它看起来很对。

Code

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

const int N = 2e5 + 5;

int n;
int M[N], f[N];
priority_queue<int> Q[N];

void merge(priority_queue<int>& a, priority_queue<int>& b) {
	static vector<int> tmp;
	if (a.size() < b.size()) {
		swap(a, b); // this is of complexity O(n) in C++
	}
	while (a.size() && b.size()) {
		int x = a.top();
		int y = b.top();
		tmp.push_back(max(x, y));
		a.pop();
		b.pop();
	}
	while (tmp.size()) {
		a.push(tmp.back());
		tmp.pop_back();
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", M + i);
	}
	for (int i = 2; i <= n; i++) {
		scanf("%d", f + i);
	}
	for (int i = n; i > 1; i--) {
		Q[i].push(M[i]);
		merge(Q[f[i]], Q[i]);
	}
	long long ans = 0;
	while (!Q[1].empty()) {
		ans += Q[1].top();
		Q[1].pop();
	}
	printf("%lld\n", ans + M[1]);
	return 0;
}

Problem C 希望

  注意到最终的可行集合非空,那么一定是树上的一个连通块。考虑点减边,问题转化成求包含每个点连通块个数的 $K$ 次方减去每条边连通块个数的 $K$ 次方。

  设 $f_{i, j}$ 表示在 $i$ 的子树内,选了一个包含 $i$ 的连通块,连通块中每个点距离 $i$ 的距离都小于等于 $j$。$g_{i, j}$ 表示子树外的情形。转移比较显然。

  注意到如果 $j \geq hei_i$,那么 $f_{i, j} = f_{i, j - 1}$。使用长链剖分优化这一部分只用维护全局加,后缀乘。但是为了快速单点询问,我们把后缀乘变成暴力前缀乘和全局乘。但是可能乘 0,这样会有点问题。乘 0 相当于对某一段后缀进行覆盖。注意到每次暴力修改都是对前缀操作,因此当修改遇到被覆盖的位置可以直接把覆盖开始的位置向后挪动。因此记录一下覆盖操作开始的位置以及值就可以维护了。

  现在考虑优化 $g$ 的转移。考虑目标是求 $g_{i, L}$ 和 $g_{i, L - 1}$,因此有用的项至多有 $hei_i + 1$ 项。不难使用后缀乘和全局加来让 $g$ 和短链或者长链合并。注意到因为逆元可能不存在所以只能搞一个前后缀合并。注意到递归短儿子的时候需要保证项数和高度同阶,所以把子节点按高度从大到小排序,然后维护 $g$ 和前缀合并的信息。

  另外 $f_{u, j}$ 的值可能在转移到父节点的时候丢掉了,所以可能需要一个可持久化。但是注意到 $f$ 可以直接支持撤销,可以设计一个简单的顺序来快速获取历史信息以及求出所有短儿子的信息和的时候不断撤销来得到所有后缀的信息。

  这样有一个问题,排序是 $O(n\log n)$ 的,求逆元会使复杂度变成 $O(n\log Mod)$。

  前者可以特判长儿子,剩下暴力基排,后者观察一下用到的逆元都是 $f_{i, \inf}$ 的逆元,这个可以暴力 dp 出来然后离线线性求逆元。

  记一下怎么离线求逆元。把要求逆元的数排成一排,求出前缀乘积,和所有乘积的逆元,然后不断推回去就能得到所有数的逆元。

  然而实际上我写的离线求逆元跑得没有 exgcd 暴力求逆元快。可能主要是因为我偷懒把一个地方 merge_short 改成 merge_long。

  成功成为 loj 除了 std 外最长代码,也就 0.714 hope

  开始我想的是记录最远距离恰好为多少,然后这个东西转移要做前缀和,看起来就不能写,sad.....。

Code

// 毒瘤出题人,标程 908 行。
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

typedef class Input {
  protected:
    const static int limit = 65536;
    FILE* file; 

    int ss, st;
    char buf[limit];
  public:

    Input() : file(NULL)	{	};
    Input(FILE* file) : file(file) {	}

    void open(FILE *file) {
      this->file = file;
    }

    void open(const char* filename) {
      file = fopen(filename, "r");
    }

    char pick() {
      if (ss == st)
        st = fread(buf, 1, limit, file), ss = 0;//, cerr << "str: " << buf << "ed " << st << endl;
      return (ss == st) ? (-1) : (buf[ss++]);
    }
} Input;

#define digit(_x) ((_x) >= '0' && (_x) <= '9')

Input& operator >> (Input& in, unsigned& u) {
  char x;
  while (~(x = in.pick()) && !digit(x));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  return in;
}

Input& operator >> (Input& in, unsigned long long& u) {
  char x;
  while (~(x = in.pick()) && !digit(x));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  return in;
}

Input& operator >> (Input& in, int& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, long long& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, double& u) {
  char x;
  while (~(x = in.pick()) && !digit(x) && x != '-');
  int aflag = ((x == '-') ? (x = in.pick(), -1) : (1));
  for (u = x - '0'; ~(x = in.pick()) && digit(x); u = u * 10 + x - '0');
  if (x == '.') {
    double dec = 1;
    for ( ; ~(x = in.pick()) && digit(x); u = u + (dec *= 0.1) * (x - '0'));
  }
  u *= aflag;
  return in;
}

Input& operator >> (Input& in, char* str) {
  char x;
  while (~(x = in.pick()) && x != '\n' && x != ' ')
    *(str++) = x;
  *str = 0;
  return in;
}

Input in (stdin);

typedef class Output {
  protected:
    const static int Limit = 65536;
    char *tp, *ed;
    char buf[Limit];
    FILE* file;
    int precision;

    void flush() {
      fwrite(buf, 1, tp - buf, file);
      fflush(file);
      tp = buf;
    }

  public:

    Output() {	}
    Output(FILE* file) : tp(buf), ed(buf + Limit), file(file), precision(6) {	}
    Output(const char *str) : tp(buf), ed(buf + Limit), precision(6) {
      file = fopen(str, "w");
    }
    ~Output() {
      flush();
    }

    void put(char x) {
      if (tp == ed)
        flush();
      *(tp++) = x;
    }

    int get_precision() {
      return precision;
    }
    void set_percision(int x) {
      precision = x;
    }
} Output;

Output& operator << (Output& out, int x) {
  static char buf[35];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    if (x < 0)
      out.put('-'), x = -x;
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, long long x) {
  static char buf[36];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    if (x < 0)
      out.put('-'), x = -x;
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, unsigned x) {
  static char buf[35];
  static char * const lim = buf + 34;
  if (!x)
    out.put('0');
  else {
    char *tp = lim;
    for ( ; x; *(--tp) = x % 10, x /= 10);
    for ( ; tp != lim; out.put(*(tp++) + '0'));
  }
  return out;
}

Output& operator << (Output& out, char x)  {
  out.put(x);
  return out;
}

Output& operator << (Output& out, const char* str) {
  for ( ; *str; out.put(*(str++)));
  return out;
}

Output& operator << (Output& out, double x) {
  int y = x;
  x -= y;
  out << y << '.';
  for (int i = out.get_precision(); i; i--, y = x * 10, x = x * 10 - y, out.put(y + '0'));
  return out;
}

Output out (stdout);

#define ll long long

void exgcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1, y = 0;
  } else {
    exgcd(b, a % b, y, x);
    y -= (a / b) * x;
  }
}

int inv(int a, int n) {
  int x, y;
  exgcd(a, n, x, y);
  return (x < 0) ? (x + n) : (x);
}

const int Mod = 998244353;

template <const int Mod = :: Mod>
class Z {
  public:
    int v;

    Z() : v(0) {	}
    Z(int x) : v(x){	}
    Z(ll x) : v(x % Mod) {	}

    friend Z operator + (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v + b.v) >= Mod) ? (x - Mod) : (x));
    }
    friend Z operator - (const Z& a, const Z& b) {
      int x;
      return Z(((x = a.v - b.v) < 0) ? (x + Mod) : (x));
    }
    friend Z operator * (const Z& a, const Z& b) {
      return Z(a.v * 1ll * b.v);
    }
    friend Z operator ~(const Z& a) {
      return inv(a.v, Mod);
    }
    friend Z operator - (const Z& a) {
      return Z(0) - a;
    }
    Z& operator += (Z b) {
      return *this = *this + b;
    }
    Z& operator -= (Z b) {
      return *this = *this - b;
    }
    Z& operator *= (Z b) {
      return *this = *this * b;
    }
    friend boolean operator == (const Z& a, const Z& b) {
      return a.v == b.v;
    } 
};

Z<> qpow(Z<> a, int p) {
  Z<> rt = Z<>(1), pa = a;
  for ( ; p; p >>= 1, pa = pa * pa) {
    if (p & 1) {
      rt = rt * pa;
    }
  }
  return rt;
}

typedef Z<> Zi;

#define Vmsg(x) #x << " = " << x

ostream& operator << (ostream& os, Zi c) {
  os << c.v;
  return os;
}

const int N = 1e6 + 5;

typedef class Edge {
  public:
    int ed, nx;

    Edge(int ed, int nx) : ed(ed), nx(nx) {	}
} Edge;

typedef class MapManager {
  public:
    vector<int> h;
    vector<Edge> E;

    void init(int n) {
      h.resize(n + 1, -1);
    }
    void add_edge(int u, int v) {
      E.emplace_back(v, h[u]);
      h[u] = (signed) E.size() - 1;	
    }
    Edge& operator [] (int p) {
      return E[p];
    }
} MapManager;

int n, L, K;
MapManager G;
int lson[N], hei[N];

void dfs1(int p, int fa) {
  for (int i = G.h[p], e = (i >= 0) ? (G[i].ed) : (-1); ~i && (e = G[i].ed, 1); i = G[i].nx) {
    if (e == fa) {
      continue;
    }
    dfs1(e, p);
    if (hei[e] > hei[p]) {
      lson[p] = e;
      hei[p] = hei[e];
    }
  }
  ++hei[p];
}

template <typename T>
class Pool {
  public:
    int maxlen;
    T* arr;
    T* top;

    void reserve(int n) {
      maxlen = n + 1;
      arr = new T[(n + 2)];
      top = arr;
    }
    T* alloc(int len) {
      maxlen -= len;
      assert(maxlen >= 0);
      T* ret = top;
      top += len;
      return ret;
    }
    void clear() {
      delete[] arr;
    }
};

Zi Fa[N], _Fa[N];

class InverseHelper {
  public:
    vector<Zi> x, px;
    vector<Zi*> ans;
    
    void solve() {
      px.reserve(x.size() + 1);
      Zi prod = 1;
      px.push_back(prod);
      int n = x.size();
      for (int i = 0; i < n; i++) {
        px.push_back(prod *= x[i]);
      }
      prod = ~prod;
      for (int i = n; i--; ) {
        *ans[i] = prod * px[i];
        prod *= x[i];
      }
    }
    void append(Zi a, Zi* y) {
      if (!a.v) {
        *y = 0;
      } else {
        x.push_back(a);
        ans.push_back(y);
      }
    }
} ih;

void dfs2(int p, int fa) {
  Fa[p] = 1;
  for (int i = G.h[p], e = (i >= 0) ? (G[i].ed) : (-1); ~i && (e = G[i].ed, 1); i = G[i].nx)	{
    if (e ^ fa) {
      dfs2(e, p);
      Fa[p] *= Fa[e];
    }
  }
  Fa[p] += 1;
  ih.append(Fa[p], _Fa + p);
}

typedef class BackupData {
  public:
    int len;
    Zi *f;
    int cov_lim;
    Zi tgm, _tgm, tga, tgc;
    vector<Zi> chg;

    void backup(Zi* s, int len) {
      chg = vector<Zi>(s, s + len);
    }
} BackupData;

Pool<Zi> plf;

typedef class dp1_t {
  public:
    int curlen;
    Zi *f;

    // tags
    int cov_lim;
    Zi tgm, _tgm, tga, tgc;

    vector<BackupData> backup;

    dp1_t() : curlen(0), f(NULL), cov_lim(N), tgm(1), _tgm(1), tga(0), tgc(0) { }

    void init_backup(BackupData& bd) {
      bd.len = curlen;
      bd.f = f;
      bd.cov_lim = cov_lim;
      bd.tgm = tgm;
      bd._tgm = _tgm;
      bd.tga = tga;
      bd.tgc = tgc;
    }
    void revoke() {
      assert(!backup.empty());
      auto& bd = backup.back();
      curlen = bd.len;
      f = bd.f;
      cov_lim = bd.cov_lim;
      tgm = bd.tgm;
      _tgm = bd._tgm;
      tga = bd.tga;
      tgc = bd.tgc;
      copy(bd.chg.begin(), bd.chg.end(), f);
      backup.pop_back();
    }

    Zi get_v(int p) {
      if (p >= cov_lim) {
        return tgc;
      }
      if (p >= curlen) {
        return get_v(curlen - 1);
      }
      return f[p] * tgm + tga;
    }

    void multiply(int p, Zi x) {
      if (p >= cov_lim) {
        cov_lim = p + 1;
        f[p] = (tgc - tga) * _tgm;
      }
      f[p] = f[p] * x + (x - 1) * tga * _tgm;
    }
    void multiply(int L, Zi x, Zi _x) { // assume last cover operation is processed
      if (!x.v) {
        cov_lim = L;
        tgc = 0;
      } else {
        tgm *= x;
        _tgm *= _x;
        tga *= x;
        tgc *= x;
        for (int i = 0; i < L; i++) {
          multiply(i, _x);
        }
      }
    }

    void init(int len) {
      f = plf.alloc(len + 2);
      f += len + 1;
      *f = 1;
      curlen = 1;
    }

    void merge(dp1_t* F, Zi _x) {
      BackupData bd;
      init_backup(bd);
      int lenF = F->curlen;
      bd.backup(f, lenF);
      backup.push_back(bd);

      for (int i = 0; i < lenF; i++) {
        multiply(i, F->get_v(i));
      }
      Zi tmp = F->get_v(lenF);
      multiply(lenF, tmp, _x);
    }

    void go() {
      BackupData bd;
      init_backup(bd);
      backup.push_back(bd);
      *--f = -tga * _tgm;
      curlen++;
      tga += 1;
      tgc += 1;
    }

    void log() {
      for (int i = 0; i <= L; i++) {
        cerr << get_v(i) << " ";
      }
      cerr << '\n';
    }
} dp1_t;

dp1_t pool1[N];
dp1_t *top1 = pool1;
dp1_t* F[N];

Zi ans = 0;
Zi ans_down[N], ans_up[N], anse_down[N], anse_up[N];

void dp_forward(int p, int fa, int len) {
  if (lson[p]) {
    dp_forward(lson[p], p, len + 1);
    F[p] = F[lson[p]];
    F[p]->go();
    for (int i = G.h[p], e = (i >= 0) ? (G[i].ed) : (-1); ~i && (e = G[i].ed, 1); i = G[i].nx)	{
      if (e == fa || e == lson[p]) {
        continue;
      }
      dp_forward(e, p, 1);
      F[e]->go();
      F[p]->merge(F[e], _Fa[e]);
    }
  } else {
    F[p] = top1++;
    F[p]->init(len);
  }
  ans_down[p] = F[p]->get_v(L);
  anse_down[p] = F[p]->get_v(L - 1);
  //  cerr << p << ": ";
  //  F[p]->log();
}

typedef class dp2_t {
  public:
    int curlen;
    Zi* f; // [L - curlen, L]

    // tags
    int cov_lim;
    Zi tga, tgm, _tgm, tgc;

    dp2_t() : curlen(0), f(NULL), cov_lim(-1), tga(0), tgm(1), _tgm(1), tgc(0) {  }
    dp2_t(int len) : curlen(len), f(plf.alloc(len + 1)), cov_lim(-1), tga(0), tgm(1), _tgm(1), tgc(0) { } 

    Zi get_v(int p) {
      if (p <= cov_lim) {
        return tgc;
      }
      return f[p] * tgm + tga;
    }
    void multiply(int p, Zi x) {
      if (p <= cov_lim) {
        cov_lim = p - 1;
        f[p] = (tgc - tga) * _tgm;
      }
      f[p] = f[p] * x + (x - 1) * tga * _tgm;
    }
    void multiply(int R, Zi x, Zi _x) { // assume last cover operation is processed
      if (!x.v) {
        cov_lim = R;
        tgc = 0;
      } else {
        tgm *= x;
        _tgm *= _x;
        tga *= x;
        tgc *= x;
        for (int i = min(L, curlen); i > R; i--) {
          multiply(i, _x);
        }
      }
    }

    void merge_short(dp1_t* F, Zi _x) {
      int Flen = F->curlen;
      for (int i = max(0, L - curlen); i < Flen && i <= L; i++) {
        multiply(L - i, F->get_v(i));
      }
      if (Flen <= L) {
        Zi tmp = F->get_v(Flen);
        multiply(L - Flen, tmp, _x);
      }
    }

    void merge_long(dp1_t* F) {
      for (int i = min(curlen, L); ~i; i--) {
        multiply(i, F->get_v(L - i));
      }
    }

    dp2_t copy(int newlen) {
      assert(newlen <= curlen);
      dp2_t rt = *this;
      rt.curlen = newlen;
      rt.f = plf.alloc(newlen + 1);
      std::copy(f, f + newlen + 1, rt.f);
      return rt;
    }

    void go() {
      ++f;
      curlen--;
      if (L <= curlen) {
        f[L] = -tga * _tgm;
      }
      tga += 1;
      tgc += 1;
    }
} dp2_t;

void dp_backward(int p, int fa, dp2_t g) {
  anse_up[p] = g.get_v(1);
  g.go();
  //  cerr << g.curlen << " " << hei[p] << '\n';
  assert(g.curlen >= hei[p]);
  ans_up[p] = g.get_v(0);
  if (!lson[p]) {
    return;
  }
  vector<int> ch;
  for (int i = G.h[p], e = (i >= 0) ? (G[i].ed) : (-1); ~i && (e = G[i].ed, 1); i = G[i].nx)	{
    if (e == fa || e == lson[p]) {
      continue;
    }
    ch.push_back(e);
  }
  sort(ch.begin(), ch.end(), [&] (int x, int y) { return hei[x] > hei[y]; });
  if (fa) {
    F[p]->revoke();
  }
  for (int t = ch.size(); t; t--) {
    F[p]->revoke();
  }
  if (ch.empty()) {
    dp_backward(lson[p], p, g);
  } else {
    dp2_t gp = g.copy(hei[ch[0]] + 1), ngp;
    gp.merge_long(F[p]);
    int sz = ch.size();
    Zi prod = 1;
    for (int i = sz - 2; ~i; i--) {
      F[ch[i]]->merge(F[ch[i + 1]], prod *= _Fa[ch[i + 1]]);
    }
    g.merge_short(F[ch[0]], prod *= _Fa[ch[0]]);
    dp_backward(lson[p], p, g);
    for (int i = 0; i < sz; i++) {
      if (i != sz - 1) {
        F[ch[i]]->revoke();
        ngp = gp.copy(hei[ch[i + 1]] + 1);
        gp.merge_long(F[ch[i + 1]]);
        ngp.merge_long(F[ch[i]]);
        dp_backward(ch[i], p, gp);
        gp = ngp;
      } else {
        dp_backward(ch[i], p, gp);  
      }
    }
  }
}

int main() {
  in >> n >> L >> K;
  G.init(n);
  for (int i = 1, u, v; i < n; i++) {
    in >> u >> v;
    G.add_edge(u, v);
    G.add_edge(v, u);
  }
  plf.reserve(n * 6 + 233);
  dfs1(1, 0);
  dfs2(1, 0);
  ih.solve();
  dp_forward(1, 0, 1);
  dp_backward(1, 0, dp2_t(hei[1] + 1));
  for (int i = 1; i <= n; i++) {
    ans += qpow(ans_up[i] * ans_down[i], K);
  }
  for (int i = 2; i <= n; i++) {
    ans -= qpow(anse_up[i] * anse_down[i], K);
  }
  out << ans.v << '\n';
  return 0;
}

posted @ 2020-03-13 21:59  阿波罗2003  阅读(275)  评论(0编辑  收藏  举报