# NOI 2019 简要题解

说好的 agc 046 呢

去年的题真难写

## Day 1

### Problem A 回家路线

暴力即可。 2e8 真的很稳。

可以按开始时间排序，然后每个点上斜率优化。

### Code

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

const int N = 2e5 + 5;

#define ll long long

const ll llf = (signed ll) (~0ull >> 3);

typedef class Point {
public:
int x;
ll y;

Point(int x = 0, ll y = 0) : x(x), y(y) { }
} Point;

Point operator - (Point a, Point b) {
return Point(a.x - b.x, a.y - b.y);
}

ll cross(Point a, Point b) {
return a.x * b.y - a.y * b.x;
}

typedef class ConvexHull {
public:
int pos;
vector<Point> stk;

ConvexHull() : pos(0) { }

void insert(int _x, ll _y) {
Point p (_x, _y);
while (!stk.empty() && stk.back().x == _x && stk.back().y >= _y)
stk.pop_back();
while (stk.size() > 1u && cross(stk.back() - stk[stk.size() - 2], p - stk.back()) <= 0)
stk.pop_back();
stk.push_back(p);
}
ll query(ll k) {
if (stk.empty()) {
return llf;
}
ll ret = llf;
for (int i = 0; i < (signed) stk.size(); i++) {
ret = min(ret, stk[i].y - stk[i].x * k);
}
pos = min(pos, (signed) stk.size() - 1);
while (pos < (signed) stk.size() - 1 && stk[pos].y - k * stk[pos].x > stk[pos + 1].y - k * stk[pos + 1].x)
pos++;
return stk[pos].y - k * stk[pos].x;
}
} ConvexHull;

typedef class Route {
public:
int s, t, p, q;

scanf("%d%d%d%d", &s, &t, &p, &q);
}

bool operator < (Route b) const {
return p < b.p;
}
} Route;

int n, m, A, B, C;
ll f[N];
Route R[N];
int ord[N];
ConvexHull con[N];

int main() {
freopen("route.in", "r", stdin);
freopen("route.out", "w", stdout);
scanf("%d%d%d%d%d", &n, &m, &A, &B, &C);
for (int i = 1; i <= m; i++) {
assert(R[i].p < R[i].q);
}
sort(R + 1, R + m + 1);
R[0].t = 1, R[0].q = 0;
for (int i = 0; i <= m; i++) {
ord[i] = i;
}
sort(ord + 1, ord + m + 1, [&] (int x, int y) { return R[x].q < R[y].q; });
ll ans = llf;
int* po = ord, *_po = ord + m + 1;
for (int i = 1; i <= m; i++) {
while (po != _po && R[*po].q <= R[i].p) {
if (f[*po] < (llf >> 1)) {
Route &r = R[*po];
con[r.t].insert(r.q, f[*po] + 1ll * A * r.q * r.q - 1ll * B * r.q);
}
po++;
}
Route& r = R[i];
f[i] = con[r.s].query(2 * A * r.p);
if (f[i] >= (llf >> 1))
continue;
f[i] += 1ll * A * r.p * r.p + 1ll * B * r.p + C;
if (r.t == n) {
ans = min(ans, f[i] + r.q);
}
}
printf("%lld\n", ans);
return 0;
}

### Problem B 机器人

显然答案是一个关于最大能选的数的不超过 $n$ 次的分段多项式。

写一个暴力打表发现涉及到的区间的长度和最长也就 17400 左右，因为一个点可能因为 +1 被加上 $O(log n)$ 次，大概总的段数也在 $10^5$ 左右。直接用点值维护分段多项式就行了。据说直接暴力维护出系数表示的多项式也能过。

### Code

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

const int N = 305;
const int Mod = 1e9 + 7;

const int inf = (~0u >> 2);

#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 x, y;
exgcd(a, Mod, x, y);
return x < 0 ? x + Mod : x;
}

typedef class Zi {
public:
int v;

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

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

vector<Zi> Inv;
vector<Zi> fac, _fac;

void prepare(int n) {
Inv.resize(n + 1);
for (int i = 1; i <= n; i++) {
Inv[i] = ~Zi(i);
}
fac.resize(n + 1);
_fac.resize(n + 1);
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i;
}
_fac[n] = ~fac[n];
for (int i = n; i; i--) {
_fac[i - 1] = _fac[i] * i;
}
}

int n, S;

typedef class Segment {
public:
int r;
vector<Zi> f;

Segment() { }
Segment(int r, vector<Zi> f) : r(r), f(f) { }

void init(Zi x) {
f.resize(S, x);
}
Zi eval(Zi n) {
static Zi L[N], R[N];
if (n.v < S) {
return f[n.v];
}
L[0] = 1;
for (int i = 0; i < S; i++) {
L[i + 1] = L[i] * (n - i);
}
R[S] = 1;
for (int i = S; i--; ) {
R[i] = R[i + 1] * (n - i);
}
Zi ret = 0;
for (int i = 0; i < S; i++) {
Zi tmp = f[i] * L[i] * R[i + 1] * _fac[i] * _fac[S - 1 - i];
if ((S - 1 - i) & 1) {
ret -= tmp;
} else {
ret += tmp;
}
}
return ret;
}
Segment operator + (const Segment& b) {
Segment s;
s.init(S);
s.r = min(r, b.r);
for (int i = 0; i < S; i++) {
s.f[i] = f[i] + b.f[i];
}
return s;
}
Segment operator * (const Segment& b) {
Segment s;
s.init(S);
s.r = min(r, b.r);
for (int i = 0; i < S; i++) {
s.f[i] = f[i] * b.f[i];
}
return s;
}

Segment& operator += (Zi x) {
for (auto& y : f)
y += x;
return *this;
}

void pre_sum() {
Zi sum = 0;
for (int i = 0; i < S; i++) {
sum += f[i];
f[i] = sum;
}
}

void shift() {
r = min(r + 1, inf);
f.insert(f.begin(), eval(Mod - 1));
f.pop_back();
}
} Segment;

typedef class Function {
public:
vector<Segment> seg;

void append(Segment s) {
seg.push_back(s);
}

void pre_sum() {
int ls = 0;
Zi sum = 0;
for (auto&s : seg) {
s.pre_sum();
s += (sum - s.eval(ls));
ls = s.r;
sum = s.eval(s.r);
}
}

Function operator * (const Function& b) {
Function f;
auto pl = seg.begin(), _pl = seg.end();
auto pr = b.seg.begin(), _pr = b.seg.end();
while (pl != _pl && pr != _pr) {
if ((*pl).r == (*pr).r) {
f.append(*(pl++) * *(pr++));
} else if ((*pl).r < (*pr).r) {
f.append(*(pl++) * *pr);
} else {
f.append(*pl * *(pr++));
}
}
assert(pl == _pl && pr == _pr);
return f;
}
Function operator + (const Function& b) {
Function f;
auto pl = seg.begin(), _pl = seg.end();
auto pr = b.seg.begin(), _pr = b.seg.end();
while (pl != _pl && pr != _pr) {
if ((*pl).r == (*pr).r) {
f.append(*(pl++) + *(pr++));
} else if ((*pl).r < (*pr).r) {
f.append(*(pl++) + *pr);
} else {
f.append(*pl + *(pr++));
}
}
assert(pl == _pl && pr == _pr);
return f;
}

Function& reserve(int l, int r) {
for (int i = seg.size(); i--; ) {
if (!i || seg[i - 1].r < r) {
seg[i].r = r;
seg.erase(seg.begin() + i + 1, seg.end());
break;
}
}
seg.push_back(Segment(inf, vector<Zi>(S, 0)));
if (l == 1) return *this;
for (int i = 0; i < (seg.size()); i++) {
if (seg[i].r >= l) {
seg.erase(seg.begin(), seg.begin() + i);
break;
}
}
seg.insert(seg.begin(), Segment(l - 1, vector<Zi>(S, 0)));
return *this;
}

Function& shift() {
for (auto& s : seg) {
s.shift();
}
seg.insert(seg.begin(), Segment(1, vector<Zi>(S, 0)));
return *this;
}

void shrink() {
vector<Segment> nseg;
int ls = 0;
for (auto& s : seg) {
if (s.r > ls) {
nseg.push_back(s);
ls = s.r;
}
}
seg = nseg;
}
} Function;

int A[N], B[N];
bool vis[N][N];
Function f0, fe, f[N][N];

int Abs(int x) {
return x < 0 ? -x : x;
}

Function solve(int l, int r) {
if (l > r) {
return fe;
}
if (vis[l][r]) {
return f[l][r];
}
vis[l][r] = true;
Function& F = f[l][r];
F = f0;
for (int i = l; i <= r; i++) {
if (Abs((i - l) - (r - i)) <= 2) {
F = F + (solve(l, i - 1) * ((i + 1 <= r) ? solve(i + 1, r).shift() : fe)).reserve(A[i], B[i]);
}
}
F.shrink();
F.pre_sum();
return F;
}

int main() {
freopen("robot.in", "r", stdin);
freopen("robot.out", "w", stdout);
scanf("%d", &n);
S = n + 2;
prepare(S + 3);
for (int i = 1; i <= n; i++) {
scanf("%d%d", A + i, B + i);
}
f0.append(Segment(inf, vector<Zi>(S, 0)));
fe.append(Segment(inf, vector<Zi>(S, 1)));
Function fans = solve(1, n);
Zi ans = fans.seg.back().eval(inf);
printf("%d\n", ans.v);
return 0;
}

### Problem C 序列

开大数组可以得到比预估更高的分数，学到了。

有一个比较好想的建图，然而大概平方版本跑不过 2e3，模拟费用流跑不过 3e5。 d1 t3 还卡常真有毒。

考虑要求每次选择选择一个 $x$ 也选择一个 $y$，这样能够很好地限制它们数量相等。

建两列点，$A_i$ 向 $B_i$ 连边，再建一个一对点 $x, x'$，连边 $(x, x', K - L , 0)$，用于限制选择不同的一对的数量。然后 $A_i$ 向 $x$ 连边，$x'$ 向 $B_i$  连边。$S$ 向 $A_i$ 连边，$B_i$ 向 $T$ 连边。

考虑怎么费用流：

• 如果 $x \rightarrow$ 未满流了，那么无论如何都相当于左边选择最大，右边再选择一个最大的，然后再判断它的流量会不会增加。
• 否则有 $4$ 种情况
• $S \rightarrow A_i \rightarrow B_i \rightarrow T$
• $S \rightarrow A_i \rightarrow x \rightarrow A_j \rightarrow B_j \rightarrow T$
• $S \rightarrow A_i \rightarrow B_i \rightarrow x' \rightarrow B_j \rightarrow T$
• $S \rightarrow A_i \rightarrow B_i \rightarrow x' \rightarrow x \rightarrow A_j \rightarrow B_j \rightarrow T$

然后瞎维护一下就行了

### Code

#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 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 (fopen("sequence.in", "r"));

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 ("sequence.out");

#define pii pair<int, int>
#define ll long long

const int inf = (signed) (~0u >> 2);

typedef class ZKW {
public:
int M;
pii mx[524288];

void init(int n, pii* w) {
for (M = 1; M < n; M <<= 1);
for (int i = 0; i < n; i++) {
mx[i + M] = w[i];
}
for (int i = n; i < M; i++) {
mx[i + M] = pii(-inf, 0);
}
for (int i = M; --i; push_up(i));
}

void push_up(int p) {
mx[p] = max(mx[p << 1], mx[p << 1 | 1]);
}
void upd(int p, int vi) {
p += M;
mx[p] = pii(vi, p - M);
for ( ; p >>= 1; push_up(p));
}
int qry() {
return mx[1].first;
}
int qryi() {
return mx[1].second;
}
} ZKW;

const int N = 2e5 + 5;

int T, n, K, L;
int cnt;
int a[N], b[N];
ZKW zs, za, zb, _za, _zb;
bool ina[N], inb[N];

void seclecta(int p) {
ina[p] = true;
cnt -= ina[p] && inb[p];
zs.upd(p, -inf);
za.upd(p, -inf);
_za.upd(p, -inf);
if (!inb[p])
_zb.upd(p, b[p]);
}
void seclectb(int p) {
inb[p] = true;
cnt -= ina[p] && inb[p];
zs.upd(p, -inf);
zb.upd(p, -inf);
_zb.upd(p, -inf);
if (!ina[p])
_za.upd(p, a[p]);
}

void solve() {
static pii tmp[N];
cnt = 0;
in >> n >> K >> L;
for (int i = 0; i < n; i++) {
in >> a[i];
}
for (int i = 0; i < n; i++) {
in >> b[i];
}
for (int i = 0; i < n; i++)
tmp[i] = pii(a[i] + b[i], i);
zs.init(n, tmp);
for (int i = 0; i < n; i++)
tmp[i] = pii(a[i], i);
za.init(n, tmp);
for (int i = 0; i < n; i++)
tmp[i] = pii(b[i], i);
zb.init(n, tmp);
for (int i = 0; i < n; i++)
tmp[i] = pii(-inf, i);
_za.init(n, tmp);
_zb.init(n, tmp);
fill(ina, ina + n, false);
fill(inb, inb + n, false);
for (int i = 1; i <= K; i++) {
if (cnt == K - L) {
int swpa = _za.qry() + zb.qry();
int swpb = za.qry() + _zb.qry();
int mx = zs.qry(), id = 0, q;
if ((q = _za.qry() + _zb.qry()) > mx)
id = 1, mx = q;
if ((q = swpa) > mx)
id = 2, mx = q;
if ((q = swpb) > mx)
id = 3, mx = q;
if (!id) {
q = zs.qryi();
seclecta(q);
seclectb(q);
} else if (id == 1) {
seclecta(_za.qryi());
seclectb(_zb.qryi());
} else if (id == 2) {
seclecta(_za.qryi());
seclectb(zb.qryi());
} else {
seclecta(za.qryi());
seclectb(_zb.qryi());
}
} else {
seclecta(za.qryi());
seclectb(zb.qryi());
}
++cnt;
}
ll ans = 0;
for (int i = 0; i < n; i++) {
ans += ina[i] * a[i];
ans += inb[i] * b[i];
}
out << ans << '\n';
}

int main() {
in >> T;
while (T--) {
solve();
}
return 0;
}

## Day 2

### Problem A 弹跳

考虑 dijkstra 的过程，每次我们加入一条边。一条边更新一个点后，一个点就不可能再被更新了。因此我们只用查询一个矩形内的所有点然后把它们删除。

可以用线段树套 set 或者 KDtree 维护。

时间复杂度 $O(n\log^2 n + m\log n)$。

假设对 $x$ 建线段树，考虑将所有查询矩形和点先按照 $y$ 从小到大排序，依次加入（矩形只用在定位到的节点上加入它的下边界），每个节点上做一次归并，这样可以预处理出每次矩形删点的 lower_bound 的结果。删点的过程显然可以用并查集优化。

理论上可以做到 $O(n\log n + m \log n)$

### Code

/**
* loj
* Problem#3159
* Accepted
* Time: 6467ms
* Memory: 51568k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean;

const int N = 7e4 + 5;
const int M = 150005;

typedef class Node {
public:
int eid, dis;

Node() {	}
Node(int eid, int dis) : eid(eid), dis(dis) {	}

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

#define pii pair<int, int>

int n, m, w, h;
int f[N];
boolean vis[N];
vector<int> G[N];
set<pii> S[N << 2];
priority_queue<Node> Q;

int xl[M], xr[M], yl[M], yr[M], W[M];

void insert(int p, int l, int r, int x, int y, int id) {
S[p].insert(pii(y, id));
if (l == r) return;
int mid = (l + r) >> 1;
if (x <= mid) {
insert(p << 1, l, mid, x, y, id);
} else {
insert(p << 1 | 1, mid + 1, r, x, y, id);
}
}

void del(int p, int l, int r, int x1, int x2, int y1, int y2, int w) {
if (l == x1 && r == x2) {
set<pii>::iterator it = S[p].lower_bound(pii(y1, 0));
while (it != S[p].end() && (*it).first <= y2) {
int id = (*it).second;
if (!vis[id]) {
vis[id] = true;
f[id] = w;
for (auto eid : G[id]) {
Q.push(Node(eid, f[id] + W[eid]));
}
}
it = S[p].erase(it);
}
return;
}
int mid = (l + r) >> 1;
if (x2 <= mid) {
del(p << 1, l, mid, x1, x2, y1, y2, w);
} else if (x1 > mid) {
del(p << 1 | 1, mid + 1, r, x1, x2, y1, y2, w);
} else {
del(p << 1, l, mid, x1, mid, y1, y2, w);
del(p << 1 | 1, mid + 1, r, mid + 1, x2, y1, y2, w);
}
}

int main() {
freopen("jump.in", "r", stdin);
freopen("jump.out", "w", stdout);
scanf("%d%d%d%d", &n, &m, &w, &h);
for (int i = 1, x, y; i <= n; i++) {
scanf("%d%d", &x, &y);
if (i ^ 1) {
insert(1, 1, w, x, y, i);
}
}
for (int i = 1, p; i <= m; i++) {
scanf("%d%d%d%d%d%d", &p, W + i, xl + i, xr + i, yl + i, yr + i);
G[p].push_back(i);
}
for (auto e : G[1]) {
Q.push(Node(e, W[e]));
}
while (!Q.empty()) {
int e = Q.top().eid;
int d = Q.top().dis;
Q.pop();
del(1, 1, w, xl[e], xr[e], yl[e], yr[e], d);
}
for (int i = 2; i <= n; i++) {
printf("%d\n", f[i]);
}
return 0;
}

### Problem B 斗主地

开始听说看过具体数学就会，后来听说打个表也没了，当时我就不一样，既没读过书，也不会打表

考虑直接计算 $E_i$ 贡献到 $E'_j$ 的系数。不妨先考虑 $i \leqslant A$ 的情形

\begin{align} \binom{n - j}{A - i} \frac{A^{\underline{A-i+1}}(n-A)^{\underline{n - j - (A- i)}}}{n^{\underline{n - j + 1}}} &= \binom{n}{A}^{-1}\binom{n-j}{A- i}\binom{j-1}{i-1} \end{align}

然后 $E'_j$ 会对 $i \leqslant A$ 的 $E_i$ 乘上这个系数进行求和。

先考虑 $E_i = i$ 的时候，提出 $\binom{n}{A}^{-1}$， 考虑这个式子的组合意义：从 $n$ 个球选出 $A$ 个球，第 $j$ 个球一定被选择，如果是第 $i$ 个被选择的球，贡献为 $i$。

考虑枚举被选择的球，不难得到它等于 $\binom{n - 1}{A - 1} + (j - 1)\binom{n - 2}{A - 2}$。

对于 $i > A$ 的时候是类似的做法。

容易发现 $E'_j$ 是关于 $j$ 的一次多项式。

当 $type = 2$ 的时候是关于 $j$ 的二次多项式。

可以暴力维护，也可以插值。

插值竞赛（确信

### Code

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

#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;
}
};

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;

int n, m, type, q;

vector<Zi> fac, _fac;

void prepare(int n) {
fac.resize(n + 1);
_fac.resize(n + 1);
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i;
}
_fac[n] = ~fac[n];
for (int i = n; i; i--) {
_fac[i - 1] = _fac[i] * i;
}
}
Zi comb(int n, int m) {
assert(n >= 0 && m >= 0);
return n < m ? 0 : fac[n] * _fac[m] * _fac[n - m];
}
Zi _comb(int n, int m) {
return _fac[n] * fac[m] * fac[n - m];
}

typedef vector<Zi> vec;

vec operator * (vec a, Zi k) {
for (auto& x : a)
x *= k;
return a;
}
vec operator + (vec a, vec b) {
if (a.size() < b.size())
a.resize(b.size());
for (int i = 0; i < (signed) b.size(); i++)
a[i] += b[i];
return a;
}

Zi eval(int A, int c) {
return A - 1 < c ? 0 : comb(n - 1 - c, A - 1 - c);
}

vec get1(int A) {
vec v (2);
v[0] = eval(n - A, 0) * A;
v[1] = eval(A, 1) + eval(n - A, 1);
return v * _comb(n, A);
}
vec get2(int A) {
vec v (3);
v[0] = eval(n - A, 0) * A * (A - 1) * ((Mod + 1) >> 1);
v[1] = eval(n - A, 1) * A;
v[2] = eval(A, 2) + eval(n - A, 2);
return v * _comb(n, A);
}

int main() {
freopen("landlords.in", "r", stdin);
freopen("landlords.out", "w", stdout);
scanf("%d%d%d", &n, &m, &type);
prepare(n + 1);
vec f, g;
if (type == 1) {
f = {1, 1};
} else {
f = {1, 3, 2};
}
for (int i = 1, a; i <= m; i++) {
scanf("%d", &a);
g = vec {f[0]};
g = g + get1(a) * f[1];
if (type == 2)
g = g + get2(a) * f[2];
f = g;
}
scanf("%d", &q);
Zi x;
while (q--) {
scanf("%d", &x.v);
Zi ans = f[0] + f[1] * (x - 1);
if (type == 2)
ans += f[2] * (x - 1) * (x - 2) * ((Mod + 1) >> 1);
printf("%d\n", ans.v);
}
return 0;
}

### Problem C I 君的探险

A：不难得到每个点周围一圈的异或和，做完了。

B：直接整体二分。

对于树的部分分考虑可以假设一个点是叶子，然后用 2 次修改 1 次询问检查它是不是，因此我们可以判断一个点是不是叶子。然后每次剥叶子就可以了。

对于一般图的情形，考虑随机一个排列，然后套用 B 的整体二分做法，如果一个点向前连了奇数条边，那么一定能找到一条边，否则可能能找到。

不难证明最坏情况下，期望能减少 $\frac{t}{3}$ 条边，其中 $t$ 是度数非 0 的点的数量。因此当有孤立点的时候复杂度有点假，需要删除孤立点。

否则的话，可以花费 $O(n\log n)$ 的代价，期望减少 $\frac{n}{3}$ 的边。因此期望的操作次数和复杂度均是 $O(m\log n)$。

至于初始孤立点我好像只会一些常数比较劣的 $O(n\log n)$ 随机化做法，加了估计操作次数会超，不过数据好像没有卡不处理初始孤立点的情况。

### Code

#include <bits/stdc++.h>
#include "explore.h"
using namespace std;

int N, M;

namespace brute {

vector<int> old;

void solve() {
old.resize(N, 0);
for (int i = 0; i < N - 1; i++) {
modify(i);
for (int j = i + 1; j < N; j++) {
int x = query(j);
if (x ^ old[j]) {
report(i, j);
old[j] = x;
}
}
}
}

}

vector<int> v;
vector<int> old;

void solve() {
v.assign(N, 0);
old.assign(N, 0);
for (int b = 1; b < N; b <<= 1) {
for (int i = 0; i < N; i++) {
if (i & b) {
modify(i);
}
}
for (int i = 0; i < N; i++) {
int x = query(i);
if (old[i] ^ x) {
v[i] |= b;
old[i] = x;
}
}
}
for (int i = 0; i < N; i++) {
if ((v[i] ^ i) < i) {
report(v[i] ^ i, i);
}
}
}

}

void dividing(int l, int r, vector<int> p)  {
if (l == r) {
for (auto x : p) {
report(l, x);
}
return;
}
if (p.empty()) {
return;
}
int mid = (l + r) >> 1;
for (int i = l; i <= mid; i++) {
modify(i);
}
vector<int> vl, vr;
for (auto x : p) {
if (x <= mid || query(x)) {
vl.push_back(x);
} else {
vr.push_back(x);
}
}
for (int i = l; i <= mid; i++) {
modify(i);
}
dividing(l, mid, vl);
dividing(mid + 1, r, vr);
}

void solve() {
vector<int> p (N - 1);
for (int i = 1; i < N; i++)
p[i - 1] = i;
dividing(0, N - 1, p);
}

}

namespace general {

vector<int> P;
vector<int> on;
vector<int> tg;
vector<bool> exist;
vector<vector<int>> G;

void answer(int x, int y) {
report(x, y);
G[x].push_back(y);
G[y].push_back(x);
M--;
if (check(x)) {
exist[x] = false;
}
if (check(y)) {
exist[y] = false;
}
}
void upd(int p) {
modify(p);
on[p] ^= 1;
}
bool qry(int p) {
bool ret = query(p);
for (auto x : G[p]) {
ret ^= on[x];
}
return ret ^ tg[p];
}

void light_on(int l, int r) {
static int ql = 0, qr = -1;
if (l < 0) {
for (int i = ql; i <= qr; i++) {
upd(P[i]);
}
ql = 0, qr = -1;
return;
}
for (int i = ql; i <= qr; i++) {
if (i < l || i > r) {
upd(P[i]);
}
}
for (int i = l; i <= r; i++) {
if (!on[P[i]]) {
upd(P[i]);
}
}
ql = l, qr = r;
}

void dividing(int l, int r, vector<int> S) {
if (S.empty()) {
return;
}
if (l == r) {
int p = P[l];
light_on(l, l);
for (auto x : S) {
if (P[x] != p && qry(P[x])) {
}
}
return;
}
int mid = (l + r) >> 1;
light_on(l, mid);
vector<int> sl, sr;
for (auto x : S) {
if (x <= mid || qry(P[x])) {
sl.push_back(x);
} else {
sr.push_back(x);
}
}
dividing(l, mid, sl);
dividing(mid + 1, r, sr);
}

void solve() {
P.resize(N);
tg.assign(N, 0);
on.assign(N, false);
exist.assign(N, true);
G.assign(N, vector<int>());
for (int i = 0; i < N; i++)
P[i] = i;
vector<int> P0 = P;
while (M) {
light_on(-1, 0);
vector<int> tmp;
for (auto x : P) {
if (exist[x]) {
tmp.push_back(x);
}
}
P = tmp;
P0.resize(P.size());
random_shuffle(P.begin(), P.end());
//    cerr << P.size() << " " << " " << M << '\n';
dividing(0, (signed) P.size() - 1, P0);
}
}

}

void explore(int N, int M) {
srand(time(NULL));
//  srand(233u);
::N = N;
::M = M;
if (N <= 500) {
brute::solve();
} else if (N % 10 == 8) {
}