我们有一个
考虑从某个格子开始,向右或向下走到相邻的格子里若干(可以是零)次所得的路径。求这样的路径中,满足起点和终点格子里的数相同的路径的数量,模
两条路径不同,若它们经过的格子(括起点和终点格子)的集合不同。
把格子按照里面的数分类。把写有整数
设定一个阈值
对所有满足
满足
我们考虑
const int mod = 998244353;
int a[401][401], dp[401][401], C[800][400];
vector<pair<int, int>> pos[160000 + 5];
int main() {
int n; cin >> n;
for (int i = 0; i < 2 * n; i++) C[i][0] = 1;
for (int i = 1; i < 2 * n; i++)
for (int j = 1; j <= min(n - 1, i); j++)
C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % mod;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++) {
cin >> a[i][j];
pos[a[i][j]].push_back({i, j});
}
long long ans = 0;
for (int i = 1; i <= n * n; i++) {
if (pos[i].size() > n) {
for (int j = 1; j <= n; j++)
for (int k = 1; k <= n; k++) {
dp[j][k] = (dp[j - 1][k] + dp[j][k - 1]) % mod;
if (a[j][k] == i) {
dp[j][k]++;
ans += dp[j][k];
}
}
} else {
for (int j = 0; j < pos[i].size(); j++)
for (int k = j; k < pos[i].size(); k++) {
int dx = pos[i][k].first - pos[i][j].first;
int dy = pos[i][k].second - pos[i][j].second;
if (dx >= 0 && dy >= 0) ans += C[dx + dy][dx];
}
}
}
cout << ans % mod << '\n';
}
集合
性质:
取一阈值
法一的总时间是
取
有
第
已知开始记录时所有人都在办公室外,现在所有人也都在办公室外。
回答
第
一共有
若
否则不妨设
回答询问时查表即可。这样的预处理的总时间是
总时间是
void solve() {
int n, m; cin >> n >> m;
vector<pair<int,int>> rec(m);
for (int i = 0; i < m; i++) cin >> rec[i].first >> rec[i].second;
vector<vector<pair<int,int>>> seg(n + 1);
vector<int> last(n + 1);
for (auto [t, p] : rec)
if (!last[p]) last[p] = t;
else {
seg[p].push_back({last[p], t});
last[p] = 0;
}
int lim = 1000;
unordered_map<int, vector<int>> ans;
for (int i = 1; i <= n; i++) {
if (seg[i].size() > lim) {
vector<int> alt_sum(n + 1);// 交错和
int sum_i = 0; // cumulative sum of time when person i was in office
bool in = false;// is person i in office now?
int prev_i = 0; // the last time when person i entered or exited office
for (auto [t, p] : rec) {
if (p == i) {
sum_i += in * (t - prev_i);
prev_i = t;
in ^= 1;
}
alt_sum[p] = sum_i + in * (t - prev_i) - alt_sum[p];
}
ans[i] = alt_sum;
}
}
int q; cin >> q;
while (q--) {
int a, b; cin >> a >> b;
if (seg[a].size() > lim)
cout << ans[a][b] << '\n';
else if (seg[b].size > lim)
cout << ans[b][a] << '\n';
else {
int t = 0, i = 0;
for (auto [l, r] : seg[a]) {
while (i < seg[b].size() && seg[b][i].second <= r) {
t += clamp(seg[b][i].second, l, r) - clamp(seg[b][i].first, l, r);
i++;
}
if (i < seg[b].size()) // seg[b][i].second > r
t += clamp(seg[b][i].second, l, r) - clamp(seg[b][i].first, l, r);
}
cout << t << '\n';
}
}
}
集合
有一个函数
回答
计算
法一: 在
法二: 对于
取一个阈值
对于询问
总时间是
给你一个有
给你
求依次处理所有询问后,每个点上所写的整数。
如果每个点的度都不大,那么暴力更新是可承受的。
注意到所有点的度之和是
取阈值
对于第
问题是怎么求出当前写在点
我们取
const int maxn = 2e5 + 5;
vector<int> g[maxn];
int last_updated[maxn];
int last_propagate[maxn];
int val[maxn];
int B;
int get_current_val(int u) {
int t = last_updated[u];
for (int v : g[u]) {
if (g[v].size() <= B) break;
t = max(t, last_propagate[v]);
}
return t == 0 ? u : val[t];
}
bool cmp(int u, int v) {
return g[u].size() > g[v].size();
}
void solve() {
int n, m, q; cin >> n >> m >> q;
for (int i = 0; i < m; i++) {
int u, v; cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++)
sort(g[i].begin(), g[i].end(), cmp);
B = sqrt(2 * m);
for (int i = 1; i <= q; i++) {
int x; cin >> x;
val[i] = get_current_val(x);
last_propagate[x] = i;
if (g[x].size() <= B)
for (int v : g[x])
last_updated[v] = i;
}
for (int i = 1; i <= n; i++)
cout << get_current_val(i) << ' ';
cout << '\n';
}