tree
考虑怎么询问一个点 x x x 的编号最大的祖先,可以先把树拍到 dfs 序上,用每个节点的编号在它的子树里取 max \max max ,然后查询 d f n [ x ] dfn[x] df n [ x ] 处的 max \max max 值即为 x x x 的编号最大的祖先。
而这时我们还需要保证取到的 max \max max 值在另一棵树上是 y y y 的祖先,我们可以只把 y y y 的祖先插入到线段树中,这样询问出来的就一定是 y y y 的祖先了。
直接暴力插入不太行,因为这题不带修,我们可以用主席树维护 y y y 到根的路径构成的线段树,时间复杂度 O ( ( n + m ) log n ) O((n+m) \log n) O (( n + m ) log n ) 。
#include <algorithm> #include <cmath> #include <cstdio> #include <iostream> #include <vector> using namespace std;const int N = 100005 ;namespace HJT_tree { struct node { int ls, rs, max; } a[N*32 ]; int root[N], lim, node_cnt; void modify (int &i, int ql, int qr, int val, int l = 1 , int r = lim) ; int query (int i, int pos, int l = 1 , int r = lim) ; } using namespace HJT_tree;int n, m;int ldfn[N], rdfn[N], dft;vector<int > ver1[N], ver2[N]; void euler_tour (int x) ;void dfs (int x, int fa) ;int main () { cin >> n >> m; for (int i = 2 ; i <= n; i++) { int f; cin >> f; ver1[f].push_back (i); } for (int i = 2 ; i <= n; i++) { int f; cin >> f; ver2[f].push_back (i); } HJT_tree::lim = n*2 ; euler_tour (1 ); dfs (1 , 0 ); int lastans = 0 ; for (int i = 1 ; i <= m; i++) { int x, y; cin >> x >> y; x = (x+lastans)%n+1 ; y = (y+lastans)%n+1 ; lastans = HJT_tree::query (root[x], ldfn[y]); cout << lastans << "\n" ; } } void euler_tour (int x) { ldfn[x] = ++dft; for (auto y : ver2[x]) euler_tour (y); rdfn[x] = ++dft; } void dfs (int x, int fa) { root[x] = root[fa]; HJT_tree::modify (root[x], ldfn[x], rdfn[x], x); for (auto y : ver1[x]) dfs (y, x); } namespace HJT_tree { #define ls(i) a[i].ls #define rs(i) a[i].rs #define lmid ((l+r)>>1) #define rmid ((l+r+2)>>1) void modify (int &i, int ql, int qr, int val, int l, int r) { a[++node_cnt] = a[i]; i = node_cnt; if (ql <= l and r <= qr) { a[i].max = max (a[i].max, val); return void (); } if (ql <= lmid) modify (ls (i), ql, qr, val, l, lmid); if (qr >= rmid) modify (rs (i), ql, qr, val, rmid, r); } int query (int i, int pos, int l, int r) { if (l == r) return a[i].max; int ans = a[i].max; if (pos <= lmid) ans = max (ans, query (ls (i), pos, l, lmid)); if (pos >= rmid) ans = max (ans, query (rs (i), pos, rmid, r)); return ans; } }
Simple Math 3
如果区间长度 l e n ≥ D len \ge D l e n ≥ D 的话肯定不满足条件,所以 i i i 就有一个枚举上界: D − 1 C − B \frac{D-1}{C-B} C − B D − 1 ,将其设为 n n n
现在保证了区间长度 l e n < D len < D l e n < D ,如果区间中有一个 D D D 的倍数,那么 ⌊ A + C i D ⌋ − ⌊ A + B i − 1 D ⌋ = 1 \left\lfloor \frac{A+C_i}{D} \right\rfloor - \left\lfloor \frac{A+B_i-1}{D} \right\rfloor = 1 ⌊ D A + C i ⌋ − ⌊ D A + B i − 1 ⌋ = 1 ,否则这个值为 0 0 0 。
考虑去除不合法的答案: n − ∑ i = 1 n ( ⌊ A + C i D ⌋ − ⌊ A + B i − 1 D ⌋ ) n - \sum_{i=1}^{n}\left( \left\lfloor \frac{A+C_i}{D} \right\rfloor - \left\lfloor \frac{A+B_i-1}{D} \right\rfloor \right) n − ∑ i = 1 n ( ⌊ D A + C i ⌋ − ⌊ D A + B i − 1 ⌋ ) 。
直接类欧几里得解决即可, 时间复杂度 O ( T log n ) O(T \log n) O ( T log n ) 。
#include <cstdio> #include <iostream> using namespace std;typedef long long ll;ll solve (ll a, ll b, ll c, ll n) { if (a == 0 ) return (b/c)*(n+1 ); if (a >= c or b >= c) return n*(n+1 )/2 *(a/c) + (n+1 )*(b/c) + solve (a%c, b%c, c, n); else { ll m = (a*n+b)/c; return n*m - solve (c, c-b-1 , a, m-1 ); } } int main () { int t; cin >> t; while (t --> 0 ) { ll a, b, c, d; cin >> a >> b >> c >> d; ll m = (d-1 )/(c-b); ll ans = m - solve (c, a, d, m) + solve (b, a-1 , d, m); cout << ans << endl; } }
Robots and Exits
因为只有机器人离开时的出口不同时方案才算不同,所以我们考虑一个机器人在哪个出口出去。
直接做的话不好做,考虑一个机器人到左边第一个出口的距离为 l l l , 到右边第一个出口的距离为 r r r ,我们把一个机器人看成一个坐标为 ( l , r ) (l,r) ( l , r ) 的点,将其放在坐标系上。
我们画一条 x = k x=k x = k 的线,就会使所有的 l ≤ k l \le k l ≤ k 的点从它左边的出口出去,画一条 y = k y=k y = k 的线就会使所有 r ≤ k r \le k r ≤ k 的点从它右边的出口出去。
将所有机器人分配一个出口的方案对应在坐标系上就是一条只能往右或往上的折线,而我们求的就是一条折线划分点集的方案数。
为了保证方案不重,我们只考虑紧贴点的折线就行。
这个可以用树状数组优化DP解决。
#include <algorithm> #include <cstdio> #include <iostream> using namespace std;typedef long long ll;const int N = 200005 ;const int P = 1e9 +7 ;namespace bit { #define lowbit(x) (x&-x) ll a[N]; int lim; void add (int x, ll val) ; ll query (int x) ; } struct node { int x, y; }; int n, m, cnt;ll pos[N], quit[N]; ll lsh[N], lcnt; node rob[N]; int main () { cin >> n >> m; for (int i = 1 ; i <= n; i++) cin >> pos[i]; for (int i = 1 ; i <= m; i++) cin >> quit[i]; for (int i = 1 ; i <= n; i++) { int p = lower_bound (quit+1 , quit+m+1 , pos[i])-quit; if (p == 1 or p == m+1 ) continue ; cnt++; rob[cnt].x = pos[i]-quit[p-1 ]; rob[cnt].y = quit[p]-pos[i]; lsh[cnt] = rob[cnt].y; } sort (rob+1 , rob+cnt+1 , [](node a, node b){ if (a.x != b.x) return a.x < b.x; else return a.y > b.y; }); sort (lsh+1 , lsh+cnt+1 ); lcnt = unique (lsh+1 , lsh+cnt+1 )-lsh-1 ; for (int i = 1 ; i <= cnt; i++) rob[i].y = lower_bound (lsh+1 , lsh+lcnt+1 , rob[i].y)-lsh; bit::lim = lcnt+1 ; bit::add (0 , 1 ); for (int i = 1 ; i <= cnt; i++) { if (rob[i].x == rob[i-1 ].x and rob[i].y == rob[i-1 ].y) continue ; bit::add (rob[i].y, bit::query (rob[i].y-1 )); } cout << bit::query (lcnt) << endl; } namespace bit { void add (int x, ll val) { for (x += 1 ; x <= lim; x += lowbit (x)) (a[x] += val) %= P; } ll query (int x) { ll ans = 0 ; for (x += 1 ; x >= 1 ; x -= lowbit (x)) (ans += a[x]) %= P; return ans; } }