重庆八中集训 day8

20220623

post

题意

给你一个有边权的节点数 \(N\) 树,然后树上 \(M\) 个关键点(可以重复),然后你标记树上的 \(K\) 个点(\(K\) 是你选的),每标记一个点你需要花费 \(C\) 的代价,然后每个关键点会寻找一个离他最近的标记点,代价是它们之间的距离,求你花费的代价的最小值。

数据范围 \(C, w\le 10 ^ 9, N\le 3\times 10 ^ 3\)

题解

看起来就很 \(N ^ 2\) 解决的样子,直接考虑 dp 解决即可,我们令 \(f_{i, j}\) 表示 \(i\) 子树内有 \(j\) 个关键点没有找到自己的点时候的花费(这 \(j\) 个点到 \(i\) 的花费也要算),然后 \(g_{i, j}\) 表示 \(i\) 子树里面自己搞得很好啊,但是从外面进来的关键点匹配进来是跟 \(j\) 匹配的,这种状态的花费。

那么答案就是 \(f_{1, 0}\)

我们考虑就按照这个意思,转移很平凡,首先考虑怎么合并两个子树,然后考虑假如当前的节点,就是这个节点是不是要加入即可。

// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
	mt19937 eng;
	void init(int Seed) {return eng.seed(Seed);}
	int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
	x = 0;
	bool f = 0;
	char ch = gc();
	while (!isdigit(ch)) f = ch == '-', ch = gc();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
	if (f) x = -x;
}
inline void reads(char *s) {
	char ch = gc();
	while (isspace(ch)) ch = gc();
	while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
	*s = 0;
	return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
	read(x);
	read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 3e3 + 10, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
	if (y < 0) {
		y %= MOD - 1;
		y += MOD - 1;
	}
	int ret = 1;
	for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
	return ret;
};
auto Mod = [] (int x) -> int {
	if (x >= MOD) return x - MOD;
	else if (x < 0) return x + MOD;
	else return x;
};
template<const int N_num, const int M_num>
struct Graph {
	int H[N_num];
	struct Edge {int to, lac, w;} e[M_num];
	inline void add_edge(int x, int y, int w) {e[*H] = {y, H[x], w};H[x] = (*H)++;}
	inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
int N, M, C, sz[MAXN], A[MAXN];
vector<int> st[MAXN];
Graph<MAXN, MAXN * 2> tr;
ll f[MAXN][MAXN], g[MAXN][MAXN], dep[MAXN];
void dfs(int u, int fa, ll fval) {
	// O(u);
	sz[u] = A[u];
	go(tr, u) if (v != fa) {
		dep[v] = dep[u] + tr.e[i].w;
		dfs(v, u, tr.e[i].w);
		sz[u] += sz[v];
	}
	static ll tmpg[MAXN], tmpf[MAXN];
	for (int i = 0; i <= sz[u]; ++i) tmpf[i] = I64_INF;
	*f[u] = 0;
	int nwsz = 0;
	go(tr, u) if (v != fa) {
		for (int j = 0; j <= nwsz; ++j) for (int k = 0; k <= sz[v]; ++k) chkmin(tmpf[j + k], f[u][j] + f[v][k]);
		for (auto &x: st[v]) g[u][x] = I64_INF;
		for (int j = 0; j <= nwsz; ++j) for (auto &x: st[v]) chkmin(g[u][x], g[v][x] + f[u][j] + j * (dep[x] - dep[u]));
		for (auto &x: st[u]) tmpg[x] = I64_INF;
		for (int j = 0; j <= sz[v]; ++j) for (auto &x: st[u]) chkmin(tmpg[x], g[u][x] + f[v][j] + j * (dep[x] - dep[u]));
		for (auto &x: st[u]) g[u][x] = tmpg[x];
		for (auto &x: st[v]) st[u].push_back(x);
		nwsz += sz[v];
		for (int j = 0; j <= nwsz; ++j) f[u][j] = tmpf[j], tmpf[j] = I64_INF;
	}
	for (int j = nwsz + A[u]; j >= A[u]; --j) f[u][j] = f[u][j - A[u]];
	for (int j = A[u] - 1; j >= 0; --j) f[u][j] = I64_INF;
	st[u].push_back(u);
	g[u][u] = I64_INF;
	for (int j = 0; j <= sz[u]; ++j) chkmin(g[u][u], f[u][j] + C);
	for (auto &x: st[u]) chkmin(f[u][0], g[u][x] += A[u] * (dep[x] - dep[u]));
	for (int j = 0; j <= sz[u]; ++j) f[u][j] += j * fval;
}
int main() {
	freopen("post.in", "r", stdin);
	// std::ios::sync_with_stdio(0);
	// cout << std::fixed << std::setprecision(8);
	// cin.tie(0);
	// cout.tie(0);
	read(N, M, C);
	qwq::init(20050112);
	tr.init();
	for (int i = 1, x, y, c; i < N; ++i) {
		read(x, y, c);
		tr.add_edge(x, y, c);
		tr.add_edge(y, x, c);
	}
	for (int i = 1, x; i <= M; ++i) read(x), A[x]++;
	dfs(1, 0, 0);
	// O(f[3][0]);
	printf("%lld\n", f[1][0]);
	// cout << (-3 / 2);
	cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
	return (0-0);
}

bigben

题意

对于这个矩阵,求他的 det。

\[a_{i, j} = \begin{cases} 1 & i = j \\ 0 & i | j \and i \not = j \\ C & other \\ \end{cases} \]

\(N \le 10 ^ {11}\)

这玩意我们考虑,写一个矩阵出来

\[\begin{bmatrix} 1& 0 &0 &0& 0 &0 \\ C& 1 &C &0& C &0\\ C&C&1&C&C&0\\ C&C&C&1&C&C\\ C&C&C&C&1&C\\ C&C&C&C&C&1\\ \end{bmatrix} \]

考虑下一行减去上一行,然后变成了一个上海森宝矩阵,然后上海森堡矩阵的置换环每个都是类似于 \(i\to j \to j - 1 \to j - 2 \to \cdots \to i + 1 \to i\) 的,然后我们做 dp 枚举置换环算 \(\det\),令 \(A = \frac{C}{1- C}\)\(f_{i} = f_{i - 1} +v \sum_{d\vert i \and d \not = i} \left(f_d -f_{d - 1}\right)\) 这个东西,然后差分,之后杜教筛即可

\[g_i = v\sum_{d | i\and d \not = 0} g_{d} \]

然后直接杜教筛即可,我们需要注意的是,要对于 \(g_1\) 这个东西特判,因为 \(g_1 = \frac{1}{1 - C}\)

// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
	mt19937 eng;
	void init(int Seed) {return eng.seed(Seed);}
	int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
	x = 0;
	bool f = 0;
	char ch = gc();
	while (!isdigit(ch)) f = ch == '-', ch = gc();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
	if (f) x = -x;
}
inline void reads(char *s) {
	char ch = gc();
	while (isspace(ch)) ch = gc();
	while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
	*s = 0;
	return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
	read(x);
	read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
	if (y < 0) {
		y %= MOD - 1;
		y += MOD - 1;
	}
	int ret = 1;
	for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
	return ret;
};
auto Mod = [] (int x) -> int {
	if (x >= MOD) return x - MOD;
	else if (x < 0) return x + MOD;
	else return x;
};
template<const int N_num, const int M_num>
struct Graph {
	int H[N_num];
	struct Edge {int to, lac, w;} e[M_num];
	inline void add_edge(int x, int y, int w) {e[*H] = {y, H[x], w};H[x] = (*H)++;}
	inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
const int MAXL = 3e7 + 10;
ll N, C, A;
int F[MAXL], G[MAXL], pri[MAXL], w[MAXL], mnp[MAXL], vf[MAXN];
bool vis[MAXL];
int SF(ll x) {
	if (x < MAXL) return F[x];
	int id = N / x;
	if (vf[id]) return vf[id];
	int ret = 0;
	for (ll l = 2, r; l <= x; l = r + 1) {
		r = x / (x / l);
		ret = (ret + (r - l + 1) % MOD * SF(x / l)) % MOD;
	}
	return vf[id] = ((ll) ret * A + G[1]) % MOD;
}
int main() {
	freopen("bigben.in", "r", stdin);
	freopen("bigben.out", "w", stdout);
	qwq::init(20050112);
	read(N, C);
	if (C == 1) {
		printf("%d\n", (N <= 2));
		return 0;
	}
	A = (ll) C * Ksm(Mod(1 - C), MOD - 2) % MOD;
	G[1] = Ksm(Mod(1 - C), MOD - 2);
	for (int i = 2; i < MAXL; ++i) {
		if (!vis[i]) pri[++*pri] = i, mnp[i] = i, w[i] = 1;
		for (int j = 1; j <= *pri && pri[j] * i < MAXL; ++j) {
			if (i % pri[j]) w[i * pri[j]] = w[i] + 1, mnp[i * pri[j]] = mnp[i] * pri[j], vis[i * pri[j]] = 1;
			else {
				vis[i * pri[j]] = 1;
				w[i * pri[j]] = w[i];
				mnp[i * pri[j]] = mnp[i];
				break;
			}
		}
	}
	const int wp[] = {0, 2, 6, 30, 210, 2310, 30030, 510510, 9699690};
	mnp[1] = 1;
	for (int i = 2; i < MAXL; ++i) {
		mnp[i] = mnp[i / mnp[i]] * wp[w[i]];
		if (mnp[i] != i) {
			G[i] = G[mnp[i]];
			continue;
		}
		G[i] = G[1];
		for (int j = 2; j * j <= i; ++j) if (i % j == 0) {
			G[i] = Mod(G[i] + G[j]);
			if (j * j != i) G[i] = Mod(G[i] + G[i / j]);
		}
		G[i] = (long long) G[i] * A % MOD;
	}
	for (int i = 1; i < MAXL; ++i) F[i] = Mod(G[i] + F[i - 1]);
	int ANS = SF(N);
	ANS = (ll) Ksm(Mod(1 - C), N % (MOD - 1)) * ANS % MOD;
	printf("%d\n", ANS);
	// cout << (-3 / 2);
	return (0-0);
}

run

题意

给你一段程序

void sol(int l, int r) {
	 if (l == r) return w(1);
	 int mid = (l + r) / 2;
	 return sol(l, mid) + sol(mid + 1, r) + w(r - l + 1);
}

其中 \(w(i) = \min\left(\left(p + 1\right)2 ^ p \vert\right)\),然后给你一个 \(01\) 串,有四种操作

  • 区间覆盖(0/1)
  • 区间反转(0变成1,1变成0)
  • 区间查询,即对于 \([l,r]\) 区间的串,转成数字为 \(N\),查询 sol(N) 的数值,对于 \(998244353\) 取模。

\(N, M\le 10 ^ 5\)

题解

我们发现,对于 \(p\),所有 \(p = \left\lceil \log_2(x)\right\rceil\)\(x\),满足 \(w(x)\) 是相等的,

考虑每个 \(x\) 的增量,对于一个 \(1\) 的最高位 \(l\),若次高位是 \(m\),那么我们对于 \(sol(x)\)\(sol(x + 1)\) 只差一个 \(w(2 ^ {l - m})\)\(w(1)\)。然后考虑我们可以找到目标串的第一个 \(1\),然后第二个 \(1\) 分别的位置 \(k, m\),先算前面第一个 \(1\) 的增量 \(\frac{(p + 1)(p + 2) 2 ^ p}{2}\),然后算第二个 \(1\) 的增量 \(2 ^ m - 1 + \frac{m 2 ^ k (2k - m + 3)}{2} + 2 ^ {k + 1}(k + 2)\),然后之后的贡献就是后面的权值乘上对应的 \(w\) 即可

// Siriqwq
#include <bits/stdc++.h>
using std::cin;
using std::cout;
using std::vector;
using std::copy;
using std::reverse;
using std::sort;
using std::get;
using std::unique;
using std::swap;
using std::array;
using std::cerr;
using std::function;
using std::map;
using std::set;
using std::pair;
using std::mt19937;
using std::make_pair;
using std::tuple;
using std::make_tuple;
using std::uniform_int_distribution;
using ll = long long;
namespace qwq {
	mt19937 eng;
	void init(int Seed) {return eng.seed(Seed);}
	int rnd(int l = 1, int r = 1000000000) {return uniform_int_distribution<int> (l, r)(eng);}
}
template<typename T>
inline void chkmin(T &x, T y) {if (x > y) x = y;}
template<typename T>
inline void chkmax(T &x, T y) {if (x < y) x = y;}
template<typename T>
inline T min(const T &x, const T &y) {return x < y ? x : y;}
template<typename T>
inline T max(const T &x, const T &y) {return x > y ? x : y;}
char buf[100000], *bufs, *buft;
#define gc() ((bufs == buft && (buft = (bufs = buf) + fread(buf, 1, 100000, stdin))), bufs == buft ? -1 : *bufs++)
template<typename T>
inline void read(T &x) {
	x = 0;
	bool f = 0;
	char ch = gc();
	while (!isdigit(ch)) f = ch == '-', ch = gc();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = gc();
	if (f) x = -x;
}
inline void reads(char *s) {
	char ch = gc();
	while (isspace(ch)) ch = gc();
	while (!isspace(ch) && ch != EOF) *(s++) = ch, ch = gc();
	*s = 0;
	return;
}
template<typename T, typename ...Arg>
inline void read(T &x, Arg &... y) {
	read(x);
	read(y...);
}
#define O(x) cerr << #x << " : " << x << '\n'
const double Pi = acos(-1);
const int MAXN = 262144, MOD = 998244353, inv2 = (MOD + 1) / 2, I32_INF = 0x3f3f3f3f;
const long long I64_INF = 0x3f3f3f3f3f3f3f3f;
auto Ksm = [] (int x, int y) -> int {
	if (y < 0) {
		y %= MOD - 1;
		y += MOD - 1;
	}
	int ret = 1;
	for (; y; y /= 2, x = (long long) x * x % MOD) if (y & 1) ret = (long long) ret * x % MOD;
	return ret;
};
auto Mod = [] (int x) -> int {
	if (x >= MOD) return x - MOD;
	else if (x < 0) return x + MOD;
	else return x;
};
template<const int N_num, const int M_num>
struct Graph {
	int H[N_num];
	struct Edge {int to, lac;} e[M_num];
	inline void add_edge(int x, int y) {e[*H] = {y, H[x]};H[x] = (*H)++;}
	inline void init() {memset(H, -1, sizeof H);*H = 0;}
};
#define go(x, y) for (int i = x.H[y], v; (v = x.e[i].to) && ~i; i = x.e[i].lac)
inline int ls(int k) {return k << 1;}
inline int rs(int k) {return k << 1 | 1;}
using ull = unsigned long long;
int N, M, p[MAXN], ip[MAXN], A[MAXN];
char s[MAXN];
int rev[MAXN * 4], cnt[MAXN * 4], val[MAXN * 4], tag[MAXN * 4];
inline int gt(int l, int r) {return Mod(p[r + 1] - p[l]);}
inline void up(int k) {
	cnt[k] = cnt[ls(k)] + cnt[rs(k)];
	val[k] = Mod(val[ls(k)] + val[rs(k)]);
}
inline void puttag(int k, int rtg, int atg, int l, int r) {
	if (~atg) {
		tag[k] = atg;
		rev[k] = 0;
		val[k] = atg ? gt(l, r) : 0;
		cnt[k] = atg ? (r - l + 1) : 0;
	}
	if (rtg) {
		val[k] = Mod(gt(l, r) - val[k]);
		cnt[k] = r - l + 1 - cnt[k];
		rev[k] ^= 1;
	}
}
inline void down(int k, int l, int r) {
	int mid = (l + r) / 2;
	puttag(ls(k), rev[k], tag[k], l, mid);
	puttag(rs(k), rev[k], tag[k], mid + 1, r);
	tag[k] = -1;
	rev[k] = 0;
}
void build(int k, int l, int r) {
	tag[k] = -1;
	if (l == r) {
		val[k] = A[l] * p[l];
		cnt[k] = A[l];
		return;
	}
	int mid = (l + r) / 2;
	build(ls(k), l, mid);
	build(rs(k), mid + 1, r);
	up(k);
}
void mfy_rev(int k, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) return puttag(k, 1, -1, l, r);
	int mid = (l + r) / 2;
	down(k, l, r);
	if (ql <= mid) mfy_rev(ls(k), l, mid, ql, qr);
	if (mid < qr) mfy_rev(rs(k), mid + 1, r, ql, qr);
	up(k);
}
void mfy_agn(int k, int l, int r, int ql, int qr, int tg) {
	if (ql <= l && r <= qr) return puttag(k, 0, tg, l, r);
	int mid = (l + r) / 2;
	down(k, l, r);
	if (ql <= mid) mfy_agn(ls(k), l, mid, ql, qr, tg);
	if (mid < qr) mfy_agn(rs(k), mid + 1, r, ql, qr, tg);
	up(k);
}
int findr(int ql, int qr) {
	if (ql > qr) return -1;
	function<int(int, int, int)> dfs = [&] (int nw, int l, int r) -> int {
		if (ql <= l && r <= qr) {
			if (!cnt[nw]) return -1;
			if (l == r) return l;
			else {
				down(nw, l, r);
				int mid = (l + r) / 2;
				if (cnt[rs(nw)]) return dfs(rs(nw), mid + 1, r);
				else return dfs(ls(nw), l, mid);
			}
		}
		down(nw, l, r);
		int mid = (l + r) / 2;
		if (mid < qr) {
			int ret = dfs(rs(nw), mid + 1, r);
			if (ret != -1) return ret;
		}
		if (ql <= mid) {
			int ret = dfs(ls(nw), l, mid);
			if (ret != -1) return ret;
		}
		return -1;
	};
	return dfs(1, 1, N);
}
int qry(int k, int l, int r, int ql, int qr) {
	if (ql <= l && r <= qr) return val[k];
	int mid = (l + r) / 2, ret = 0;
	down(k, l, r);
	if (ql <= mid) ret = qry(ls(k), l, mid, ql, qr);
	if (mid < qr) ret = Mod(ret + qry(rs(k), mid + 1, r, ql, qr));
	return ret;
}
inline int getf(int x) {return ((x + 1LL) * p[x] + 1) % MOD;}
int ans(int l, int r) {
	int k = findr(l, r);
	// 没有 1 存在
	// O(k);
	if (k == -1) return 0;
	int m = findr(l, k - 1);
	if (m == -1) {
		// 2 的 幂次
		k -= l;
		return (k + 1LL) * (k + 2LL) / 2 % MOD * p[k] % MOD;
	}
	m -= l;
	k -= l;
	// 最高位贡献
	int ANS = (k + 1LL) * (k + 2LL) / 2 % MOD * p[k] % MOD;
	// 加到第一个
	ANS = Mod(ANS + getf(k + 1));
	// 后面除了最高位和次高位的贡献
	ANS = (ANS + getf(k - m) * ((ll) qry(1, 1, N, l, r) * ip[l] % MOD - p[k] - p[m] + 2LL * MOD)) % MOD;
	// 还有就是怎么加到次高位
	ANS = (ANS + p[m] - 1 + p[k] * (k * 2LL - m + 3) % MOD * m % MOD * inv2) % MOD;
	return ANS;
}
int main() {
	freopen("run.in", "r", stdin);
	freopen("run.out", "w", stdout);
	// std::ios::sync_with_stdio(0);
	// cout << std::fixed << std::setprecision(8);
	// cin.tie(0);
	// cout.tie(0);
	qwq::init(20050112);
	read(N, M);
	reads(s + 1);
	for (int i = 1; i <= N; ++i) A[i] = s[i] - '0';
	p[0] = ip[0] = 1;
	for (int i = 1; i < MAXN; ++i) p[i] = p[i - 1] * 2 % MOD, ip[i] = (ll) ip[i - 1] * inv2 % MOD;
	build(1, 1, N);
	for (int i = 1, opt, l, r; i <= M; ++i) {
		read(opt, l, r);
		if (opt == 1) mfy_rev(1, 1, N, l, r);
		else if (opt == 2) mfy_agn(1, 1, N, l, r, 0);
		else if (opt == 3) mfy_agn(1, 1, N, l, r, 1);
		else printf("%d\n", ans(l, r));
	}
	// cout << (-3 / 2);
	cerr << ((double) clock() / CLOCKS_PER_SEC) << '\n';
	return (0-0);
}
posted @ 2022-06-23 21:37  siriehn_nx  阅读(111)  评论(0编辑  收藏  举报