CSU-ACM2025 暑假集训 训练赛1 题解
树的直径性质:所有直径的中点重合。(中点可以在顶点,也可以在边上)
可以考虑边转点,将 \(e(u,v)\) 转化为 \(e(u,x)\) 和 \(e(x,v)\),此时所有直径的中点都在顶点上,便于处理。
设直径中心为根节点 \(root\),原树的直径长度为 \(len\),在新树上为直径一端到 \(root\) 的距离。
问题转化为,在树上选择一个大小大于等于 \(2\) 点集 \(S\),满足:若 \(x\in S\),则 \(x\) 到 \(root\) 的距离为 \(len\);若 \(x\in S,y\in S\),则 \(x,y\) 的最近公共祖先为 \(root\)。求这样的点集的方案数。
设 \(v\in V\) 为 \(root\) 的子节点,设 \(c_v\) 为 \(v\) 子树中到 \(root\) 距离为 \(len\) 的顶点个数,设 \(s\) 为 \(c\) 的总和。
每个子树至多选一个,再减去选一个和不选的方案数,答案为:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define i128 __int128
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 4e5 + 5;
int n, tot;
vector<int> to[N];
int dis[N], pre[N];
void dfs(int k, int fa)
{
dis[k] = dis[fa] + 1, pre[k] = fa;
for(auto v : to[k])
{
if(v == fa) continue;
dfs(v, k);
}
}
int dep[N], sum[N];
void dfs2(int k, int fa)
{
dep[k] = 1, sum[k] = 1;
for(auto v : to[k])
{
if(v == fa) continue;
dfs2(v, k);
if(dep[v] + 1 > dep[k]) dep[k] = dep[v] + 1, sum[k] = sum[v];
else if(dep[v] + 1 == dep[k]) sum[k] += sum[v];
}
}
const ll mod = 998244353;
void solve()
{
n = read(), tot = n;
for(int i = 1; i < n; ++i)
{
int x = read(), y = read();
++tot;
to[x].emplace_back(tot);
to[tot].emplace_back(x);
to[y].emplace_back(tot);
to[tot].emplace_back(y);
}
dfs(1, 0);
int p1 = 0;
for(int i = 1; i <= tot; ++i)
if(dis[i] > dis[p1]) p1 = i;
for(int i = 1; i <= tot; ++i) dis[i] = 0, pre[i] = 0;
dfs(p1, 0);
int p2 = 0;
for(int i = 1; i <= tot; ++i)
if(dis[i] > dis[p2]) p2 = i;
int lim = dis[p2] / 2, now = p2;
while(lim--) now = pre[now];
dfs2(now, 0);
int s = 0;
ll ans = 1;
lim = dis[p2] / 2;
for(auto v : to[now])
{
if(dep[v] == lim) s += sum[v], ans = ans * (sum[v] + 1) % mod;
}
ans = (ans - 1 - s + mod) % mod;
printf("%lld\n", ans);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
设 \(dp_{l,r}\) 表示区间 \([l,r]\) 完全配对的方案数,转移时考虑枚举与 \(l\) 配对的 \(k\),此时 \([l,k],[k+1,r]\) 各自的配对顺序相互独立,有转移:
当 \(k=l+1\) 或者 \(k=r\) 时特殊处理。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 405;
const ll mod = 998244353;
int n, m;
int vis[N][N];
ll jc[N], inv[N];
ll qpow(ll a, ll b, ll mod)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
void init()
{
jc[0] = jc[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= 400; ++i) jc[i] = jc[i - 1] * i % mod;
inv[400] = qpow(jc[400], mod - 2, mod);
for(int i = 399; i >= 2; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int n, int m)
{
if(n < 0 || m < 0 || n < m) return 0;
return jc[n] * inv[m] % mod * inv[n - m] % mod;
}
ll dp[N][N];
void solve()
{
n = read(), m = read();
for(int i = 1; i <= m; ++i)
{
int a = read(), b = read();
vis[a][b] = 1;
}
for(int i = 1; i < n + n; ++i) if(vis[i][i + 1]) dp[i][i + 1] = 1;
for(int len = 4; len <= n + n; len += 2)
for(int l = 1, r = len; r <= n + n; ++l, ++r)
{
if(vis[l][r]) dp[l][r] = dp[l + 1][r - 1];
if(vis[l][l + 1]) dp[l][r] += dp[l + 2][r] * ((r - l - 1) / 2 + 1) % mod;
for(int k = l + 3; k < r; k += 2)
if(vis[l][k]) dp[l][r] += dp[l + 1][k - 1] * dp[k + 1][r] % mod * C((r - l + 1) / 2, (r - k) / 2) % mod;
dp[l][r] %= mod;
}
printf("%lld\n", dp[1][n + n]);
}
int main()
{
init();
int T = 1;
while(T--) solve();
return 0;
}
DP做法:发现类似斯特林数定义,用斯特林数的递推:设 \(dp_{i,j}\) 表示将前 \(i\) 个数分成 \(j\) 组的方案数。
转移时考虑新加入一个数,放在已有的组还是新组,已有的组中有 \(\lfloor\frac{i-1}{m}\rfloor\) 个组不能选。
组合意义-二项式反演做法:设 \(cnt_i\) 表示 \([1,n]\) 中模 \(m\) 余 \(i\) 的数的个数,设 \(f(k)\) 表示分成 \(k\) 组,组与组之间有序可为空,设 \(g(k)\) 表示分成 \(k\) 组,组与组之间有序不可为空,则有:
由二项式反演得:
\(f(k)\) 和 \(g(k)\) 都可以在 \(O(n^2)\) 求出。由于 \(g(k)\) 为组与组之间有序的方案数,最终答案要除以 \(k!\)。
优化:注意到 \(cnt_i\) 的取值至多两种,所以 \(f(k)\) 可以在 \(O(n\log n)\) 的时间内求出(快速幂),\(g(k)\) 可以使用NTT求解,复杂度为 \(O(n\log n)\)。
DP做法:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 5005;
const ll mod = 998244353;
int n, m;
ll dp[N][N];
void solve()
{
int n = read(), m = read();
dp[0][0] = 1;
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= i; ++j)
dp[i][j] = (dp[i - 1][j - 1] + 1ll * (j - (i - 1) / m) * dp[i - 1][j] % mod + mod) % mod;
for(int i = 1; i <= n; ++i) printf("%lld\n", dp[n][i]);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
二项式反演做法:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 5005;
const ll mod = 998244353;
int n, m;
int vis[N][N];
ll jc[N], inv[N];
ll qpow(ll a, ll b, ll mod)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
void init()
{
jc[0] = jc[1] = inv[0] = inv[1] = 1;
for(int i = 2; i <= 5001; ++i) jc[i] = jc[i - 1] * i % mod;
inv[5001] = qpow(jc[5001], mod - 2, mod);
for(int i = 5000; i >= 2; --i) inv[i] = inv[i + 1] * (i + 1) % mod;
}
ll C(int n, int m)
{
if(n < 0 || m < 0 || n < m) return 0;
return jc[n] * inv[m] % mod * inv[n - m] % mod;
}
ll f[N], g[N];
void solve()
{
int n = read(), m = read();
int cnt = n / m;
int x = n - cnt * m, y = m - x; //x个cnt+1,y个cnt
for(int k = cnt; k <= n; ++k)
{
f[k] = qpow(C(k, cnt) * jc[cnt] % mod, y, mod) * qpow(C(k, cnt + 1) * jc[cnt + 1] % mod, x, mod) % mod;
}
for(int k = 1; k <= n; ++k)
{
for(int i = 1; i <= k; ++i)
{
if((k - i) & 1) g[k] -= C(k, i) * f[i] % mod;
else g[k] += C(k, i) * f[i] % mod;
}
printf("%lld\n", (g[k] % mod + mod) % mod * inv[k] % mod);
}
}
int main()
{
init();
int T = 1;
while(T--) solve();
return 0;
}
注意到 \(A_i=\frac{2}{9}(10^i-1)\),要求最小的 \(i\) 使得 \(k\mid \frac{2}{9}(10^i-1)\),即 \(\frac{2}{9}(10^i-1)\equiv 0\bmod k\)。
引理一:若 \(ax\equiv0\bmod b\),则 \(x\equiv0\bmod \frac{b}{\gcd(a,b)}\)。
引理二:若 \(\frac{x}{a}\equiv 0\bmod b\),则 \(x\equiv 0\bmod ab\)。
记 \(m=\frac{9k}{\gcd(9k,2)}\)。
于是可以将问题转化为求最小的 \(i\) 使得 \(10^i\equiv 1\bmod m\)。
引理三:若 \(a^x\equiv 1\bmod b\),则 \(\gcd(a,b)=1\)。
所以当 \(\gcd(10,m) \neq 1\) 时,无解。
否则,由于 \(\gcd(10,m)=1\),所以有 \(10^{\varphi(m)}\equiv 1\bmod m\)。
那么最小整数解一定是 \(\varphi(m)\) 的约数,枚举约数判断。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define i128 __int128
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 100;
int p[N], tot;
ll qpow(ll a, ll b, ll mod)
{
ll ans = 1;
while(b)
{
if(b & 1) ans = ans * a % mod;
b >>= 1;
a = a * a % mod;
}
return ans;
}
void solve()
{
tot = 0;
int K = read();
if(K & 1) K *= 2;
K = K / 2 * 9;
int k = K;
for(int i = 2; i * i <= k; ++i)
{
if(k % i == 0)
{
p[++tot] = i;
while(k % i == 0) k /= i;
}
}
if(k > 1) p[++tot] = k;
int phi = K;
for(int i = 1; i <= tot; ++i) phi = phi / p[i] * (p[i] - 1);
int ans = 0x3f3f3f3f;
for(int i = 1; i * i <= phi; ++i)
{
if(phi % i == 0)
{
if(qpow(10, i, K) == 1) ans = min(ans, i);
if(qpow(10, phi / i, K) == 1) ans = min(ans, phi / i);
}
}
if(ans == 0x3f3f3f3f) printf("-1\n");
else printf("%d\n", ans);
}
int main()
{
int T = read();
while(T--) solve();
return 0;
}
简要题意:
给定 \(n\times n\) 的矩阵 \(a\),判断是否满足以下条件:
- \(a_{i,i}=0\).
- \(a_{i,j}=a_{j,i}\)。
- \(a_{i,j}\le \max\{a_{i,k},a_{k,j}\}\)。
(\(1\le n\le 2500\))
题解:
条件一二容易判断,考虑条件三的意义:建边 \((i,j,a_{i,j})\),对于任意的 \(i,j\),都没有 \(k\) 使得 \(k\) 与两个点的距离都小于 \(i,j\) 的距离。
注意到条件三可以扩展:\(a_{i,j}\le \max\{a_{i,k},a_{k,j}\}\le \max\{a_{i,k},\max\{a_{k,l},a_{l,j}\}\}=\max\{a_{i,k},a_{k,l},a_{l,j}\}\)。
在图上就是,\(a_{i,j}\) 是 \(i\) 到的 \(j\) 的最短路径。
考虑 \(kruskal\) 求最小生成树的结论:可能成为最小生成树的边的边集中,相同边权的边将图分为相同的联通块。
在本题中就是,每条边都可能成为最小生成树的边,在只考虑 \(<a_{i,j}\) 的边时,\(i\) 与 \(j\) 不联通。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define i128 __int128
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 2505;
int n;
int a[N][N];
struct Edge
{
int i, j, v;
Edge(){ i = j = v = 0; }
Edge(int i, int j, int v): i(i), j(j), v(v){}
bool friend operator < (const Edge &a, const Edge &b)
{
return a.v < b.v ;
}
}E[N * N];
int tot;
int f[N];
int find(int x){ return (x == f[x]) ? x : (f[x] = find(f[x])); }
void merge(int x, int y)
{
x = find(x), y = find(y);
if(x == y) return ;
f[y] = x;
}
void solve()
{
n = read();
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
a[i][j] = read();
for(int i = 1; i <= n; ++i)
{
if(a[i][i] != 0)
{
printf("NOT MAGIC\n");
return ;
}
}
for(int i = 1; i <= n; ++i)
for(int j = 1; j <= n; ++j)
if(a[i][j] != a[j][i])
{
printf("NOT MAGIC\n");
return ;
}
for(int i = 1; i <= n; ++i) f[i] = i;
for(int i = 1; i <= n; ++i)
for(int j = i + 1; j <= n; ++j)
E[++tot] = (Edge){i, j, a[i][j]};
sort(E + 1, E + tot + 1);
int last = 0;
for(int i = 1; i <= tot; ++i)
{
if(E[i].v != E[i - 1].v )
{
for(int j = last + 1; j < i; ++j) merge(E[j].i , E[j].j );
last = i - 1;
}
int x = find(E[i].i ), y = find(E[i].j );
if(x == y)
{
printf("NOT MAGIC\n");
return ;
}
}
printf("MAGIC\n");
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
当 \(x=0\) 时,只有 \(y=0\) 可行;当 \(y=0\) 时,只有 \(x=0\) 可行,以下讨论默认 \(1\le x\le P-1,1\le y\le P-1\)。
前置知识:原根 - OI Wiki
简化版前置知识:
- 素数 \(P\) 一定存在原根 \(g\)。
- \(g,g^2,g^3\cdots,g^{P-1}\) 在模 \(P\) 意义下恰好取遍 \(1\sim P-1\)。
- 若 \(g^a\equiv g^b\bmod P\),则 \(a\equiv b\bmod P-1\)。
回到本题:(本题不需要求原根,只使用了原根的性质)
考虑 \(P\) 的原根 \(g\):\(g^1,g^2\cdots,g^{P-1}\) 取遍 \(1\sim P-1\),设 \(x=g^a,y=g^b\),则要满足 \(g^{an}\equiv g^{b}\bmod P\),等价于 \(an\equiv b\bmod P-1\)。
枚举 \(a\),由裴蜀定理可知,应有 \(b\mid gcd(a,P-1)\),答案为
枚举 \(gcd(a,P-1)=d\),设 \(f(d)=\sum_{a}[gcd(a,P-1)=d]\),答案为
求解 \(f(d)\):设 \(g(d)=\sum_{a}[gcd(a,P-1)\mid d]=\frac{P-1}{d}\)。
则有 \(g(d)=f(d)+f(2d)+\cdots\)。
倒序 \(O((P-1的约数个数)^2)\) 容斥即可。
\(10^{12}\) 至多约有 \(7000\) 约数。
进一步化简:\(f(d)=\sum_{a}[gcd(a,P-1)=d]=\sum_{a}[gcd(a,\frac{P-1}{d})=1]=\varphi(\frac{P-1}{d})\)。
每次 \(O(\sqrt{d})\) 求解 \(\varphi\) 函数也可以通过。
理论上存在将 \(P-1\) 质因子分解后 \(DFS\) 枚举约数,同时求出约数的 \(\varphi\) 函数,将复杂度降到 \(O(约数个数)\) 。
容斥做法
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
ll read()
{
ll x = 0; bool f = false; char c = getchar();
while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const ll mod = 998244353;
ll P;
vector<ll> d, f, g;
void solve()
{
P = read();
ll t = sqrtl(P - 1);
for(ll i = 1; i <= t; ++i)
{
if((P - 1) % i == 0)
{
d.emplace_back(i);
if((P - 1) / i != i) d.emplace_back((P - 1) / i);
}
}
sort(d.begin(), d.end());
f.resize(d.size()), g.resize(d.size());
for(int i = 0; i < d.size(); ++i) g[i] = (P - 1) / d[i];
for(int i = (int)(d.size()) - 1; i >= 0; --i)
{
f[i] = g[i];
for(int j = i + 1; j < d.size(); ++j)
if(d[j] % d[i] == 0) f[i] -= f[j];
f[i] %= mod;
}
ll ans = 0;
for(int i = 0; i < d.size(); ++i) ans += (P - 1) / d[i] % mod * f[i] % mod;
printf("%lld\n", (ans + 1) % mod);
}
int main()
{
int T = 1;
while(T--) solve();
return 0;
}
学习分治必做的经典好题。
由于每行每列恰好有一个怪兽,设 \(p_i\) 为第 \(i\) 行的怪兽所处的列数。
考虑第 \(l\) 行到第 \(r\) 行的怪兽合法的条件:
即区间极差等于区间长度-1。
由于 \(max/min\) 函数不满足消去律,考虑分治,从 \(mid\) 位置向两端扩展。
设当前分治区间为 \([l,r]\),只考虑跨过 \([mid,mid+1]\) 的区间是否合法。
设 \(mxl[i]\) 表示 \([i,mid]\) 的最大值,\(mnl[i]\) 表示 \([i,mid]\) 的最小值,\(mxr[i]\) 表示 \([mid+1,i]\) 的最大值,\(mnr[i]\) 表示 \([mid+1,i]\) 的最小值。
合法区间 \([l',r']\) 一定属于四种情况之一:
- 最大值,最小值都在 \([l',mid]\)。
- 最大值,最小值都在 \([mid+1,r']\)。
- 最大值在 \([l',mid]\),最小值在 \([mid+1,r']\)。
- 最小值在 \([l',mid]\),最大值在 \([mid+1,r']\)。
case 1:最大值,最小值都在 \([l',mid]\)。
枚举 \(l'\in [l,mid]\),极差为 \(mxl[l']-mnl[l']\),合法右端点 \(r'\) 应满足 :
- \(r'\ge mid+1\)。
- \(mxl[l']-mnl[l']=r'-l'\)。
- \(mxr[r']<mxl[l']\)。
- \(mnr[r']>mnl[l']\)。
可以 \(O(1)\) 判断,case 2 同理。
case 3:最大值在 \([l',mid]\),最小值在 \([mid+1,r']\)。
枚举 \(l'\in [l',mid]\),合法右端点 \(r'\) 应满足:
- \(r'\ge mid+1\)。
- \(mxl[l']-mnr[r']=r'-l'\),即 \(mxl[l']+l'=mnr[r']+r'\)。
- \(mxl[l']>mxr[r']\)。
- \(mnl[l']>mnr[r']\)。
当固定 \(l'\) 时,满足条件:\(mxl[l']>mxr[r']\) 的 \(r'\) 是 \([mid+1,r]\) 的一段前缀;
满足条件 \(mnl[l']>mnr[r']\) 的 \(r'\) 是 \([mid+1,r]\) 的一段后缀,且随着 \(l'\) 减小单调移动,可以用两个指针记录并维护区间交,用桶记录值为 \(mnr[l']+l'\) 的 \(r'\) 的个数。case 4 同理。
由于求解 \([l,r]\) 的答案的复杂度为 \(O(r-l+1)\),分治有 \(\log n\) 层,总复杂度为 \(O(n\log n)\)。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define re register
#define ll long long
inline int read()
{
int x = 0, f = 0; char c = getchar();
while (c < '0') f |= (c == '-'), c = getchar();
while (c >= '0') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
return f ? -x : x;
}
const int N = 3e5 + 10;
int n, a[N], maxx[N], minn[N];
ll ans;
int cnt[N << 2];
void solve(int l, int r)
{
if(l == r) { ++ans; return ; }
int mid = (l + r) >> 1;
solve(l, mid), solve(mid + 1, r);
maxx[mid] = minn[mid] = a[mid];
maxx[mid + 1] = minn[mid + 1] = a[mid + 1];
for (re int i = mid - 1; i >= l; --i)
{
maxx[i] = max(a[i], maxx[i + 1]);
minn[i] = min(a[i], minn[i + 1]);
}
for (re int i = mid + 2; i <= r; ++i)
{
maxx[i] = max(a[i], maxx[i - 1]);
minn[i] = min(a[i], minn[i - 1]);
}
// 需要找的区间是,r - l == maxx - minn
// 转化为r = maxx - minn + l或者l = r - maxx + minn
// 最大值最小值都在左边
int R = mid;
for (re int i = mid; i >= l; --i)
{
while (R < r && maxx[R + 1] < maxx[i] && minn[R + 1] > minn[i]) ++R;
if(maxx[i] - minn[i] + i >= mid + 1 && maxx[i] - minn[i] + i <= R) ++ans;
}
// 最大值最小值都在右边
int L = mid + 1;
for (re int i = mid + 1; i <= r; ++i)
{
while (L > l && maxx[L - 1] < maxx[i] && minn[L - 1] > minn[i]) --L;
if(i - maxx[i] + minn[i] >= L && i - maxx[i] + minn[i] <= mid) ++ans;
}
// 最大值在左边,最小值在右边
// r - l == maxx - minn --> l + maxx = r + minn
// 开一桶记录右边每一个点的r + minn,然后指针控制满足要求的区间
L = mid + 1, R = mid;
for (re int i = mid; i >= l; --i)
{
while (R < r && maxx[R + 1] < maxx[i]) ++R, ++cnt[R + minn[R]];
while (L <= R && minn[L] > minn[i]) --cnt[L + minn[L]], ++L;
ans += cnt[i + maxx[i]];
}
while (L <= R) --cnt[L + minn[L]], ++L;
// 最小值在左边,最大值在右边
// r - l == maxx - minn --> l - minn = r - maxx
// 开桶记录右边每一个点的r - maxx
L = mid + 1, R = mid;
for (re int i = mid; i >= l; --i)
{
while (R < r && minn[R + 1] > minn[i]) ++R, ++cnt[R - maxx[R] + n];
while (L <= R && maxx[L] < maxx[i]) --cnt[L - maxx[L] + n], ++L;
ans += cnt[i - minn[i] + n];
}
while (L <= R) --cnt[L - maxx[L] + n], ++L;
}
int main()
{
n = read();
for (re int i = 1; i <= n; ++i)
{
int x = read(), y = read();
a[x] = y;
}
solve(1, n);
printf ("%lld\n", ans);
return 0;
}

浙公网安备 33010602011771号