题:给出一棵 n n n结点的树,每个结点上有一个字符, ( x , y ) (x,y) (x,y)表示从点 x x x到点 y y y路径上字符组成的字符串。有 q q q个查询,每个查询给出 a , b , c , d a,b,c,d a,b,c,d,求 ( a , b ) (a,b) (a,b)和 ( c , d ) (c,d) (c,d)的最长公共前缀。( n , q ≤ 300000 n,q\le300000 n,q≤300000)。
解:定义串 s s s的哈希函数为 h s ( s ) = ∑ i = 1 n ( s i − ′ 0 ′ + 1 ) ∗ b a s e i hs(s)=\sum\limits_{i=1}^n(s_i-'0'+1)*base^i hs(s)=i=1∑n(si−′0′+1)∗basei
定义 f ( x , y ) f(x,y) f(x,y)为 ( x , y ) (x,y) (x,y)的哈希值。对于每一个结点 u u u,记录 f ( 1 , u ) f(1,u) f(1,u)和 f ( u , 1 ) f(u,1) f(u,1)。设点 s s s为 t t t的祖先,则 f ( s , t ) = ( f ( 1 , t ) − f ( 1 , f a s ) ) ∗ i n v ( b a s e d e p s − 1 ) f(s,t)=(f(1,t)-f(1,fa_s))*inv(base^{dep_s-1}) f(s,t)=(f(1,t)−f(1,fas))∗inv(basedeps−1) f ( t , s ) = f ( t , 1 ) − b a s e d e p t − d e p s ∗ f ( s , 1 ) f(t,s)=f(t,1)-base^{dep_t-dep_s}*f(s,1) f(t,s)=f(t,1)−basedept−deps∗f(s,1)同时,若 s s s和 t t t互不为祖先,求出其 l c a lca lca拆分出两条路径并采用,仍然可用上述方法 O ( 1 ) O(1) O(1)求出哈希值。
从而,对于每个查询,求出 l c a ( a , b ) lca(a,b) lca(a,b)和 l c a ( c , d ) lca(c,d) lca(c,d),二分 ( a , b ) (a,b) (a,b)和 ( c , d ) (c,d) (c,d)最长公共前缀的长度,长链剖分求 b b b和 d d d的 k k k级祖先,判断哈希值是否相等即可,时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)。
有 n n n朵花,第 i i i朵花最初高度为 h i h_i hi,每天晚上长高 a i a_i ai。每天白天可以使用 k k k次魔法,魔法选择一朵花 i i i,将其高度变为 m a x ( 0 , h i − p ) max(0,h_i-p) max(0,hi−p)。 m m m天后所有花中的最大高度最小是多少?
n ≤ 100000 ; m ≤ 5000 ; k ≤ 10 ; p , a i , h i ≤ 1 e 9 n\le100000;m\le5000;k\le10;p,a_i,h_i\le1e9 n≤100000;m≤5000;k≤10;p,ai,hi≤1e9
二分 m m m天后花的最大高度 H H H,判断 m m m天后花的高度是否都 ≤ H \le H ≤H。由 h i = m a x ( 0 , h i − p ) h_i=max(0,h_i-p) hi=max(0,hi−p)的限制,在 h i ≤ p h_i\le p hi≤p时,会浪费 p − h i p-h_i p−hi的长度,因此需要考虑尽可能少的浪费长度。
问题转换:有 n n n朵花,第 i i i朵花最初高度为 H H H,每天白天长矮 a i a_i ai。每天晚上可以使用 k k k次魔法,魔法选择一朵花 i i i,将其高度增加 p p p。 m m m天后花的高度是否大于 h i h_i hi?过程中花的高度要 ≥ 0 \ge0 ≥0。每次贪心选择最快会长矮为负数的花使用魔法长高即可。
− 过 程 中 花 的 高 度 ≥ 0 的 条 件 仍 然 充 满 疑 问 中 − -过程中花的高度\ge0的条件仍然充满疑问中- −过程中花的高度≥0的条件仍然充满疑问中−
给定 0 , 1 , . . . , n − 1 0,1,...,n-1 0,1,...,n−1的排列 P 0 P 1 . . . P n − 1 P_0P_1...P_{n-1} P0P1...Pn−1。执行交换 p i p_i pi和 p ( i + p i ) m o d n p_{(i+p_i)\bmod n} p(i+pi)modn最多 200000 200000 200000次,使得排列升序。
解:先不停执行 p i = 1 p_i=1 pi=1将 0 0 0交换到位置 n − 1 n-1 n−1,然后把 1 , 2 , . . . , n − 1 1,2,...,n-1 1,2,...,n−1逐个交换到位置 n − 2 , n − 3 , . . . , 0 n-2,n-3,...,0 n−2,n−3,...,0(重复交换一个位置,总是得到不同的数,但由于 0 , 1 , 2 , . . . , x 0,1,2,...,x 0,1,2,...,x被排在交换位置的 1 x + 1 1~x+1 1 x+1步中,故当 a n − x − i = x a_{n-x-i}=x an−x−i=x之前不会打乱顺序)。最后,将得到一个 n − 1 , n − 2 , . . . , 2 , 1 , 0 n-1,n-2,...,2,1,0 n−1,n−2,...,2,1,0的降序排列,此时,因为 1 1 1可以自由移动,因此对于 2 , 3 , 4 , . . . , n − 1 2,3,4,...,n-1 2,3,4,...,n−1先把 1 1 1移动到对应要交换的位置,然后再进行交换。
首先,把一个格子分成四块,每一块格子可以唯一表示一个 L L L形。
其次,观察 L L L形的移动,一个重心可以向其周围 7 7 7个位置移动(除了重心格子角的那边)。
于是可以将问题转换为重心的移动,将 L L L形转换为新棋盘中重心的坐标。观察上述移动策略可以发现,若重心坐标为 x , y x,y x,y且 x ≠ y x\neq y x=y,则答案为 m a x ( ∣ x ∣ , ∣ y ∣ ) max(|x|,|y|) max(∣x∣,∣y∣),否则为 ∣ x ∣ + 1 |x|+1 ∣x∣+1。
首先,若两边同色,则所有方块颜色必然相同。
其次,若 [ 1 , L ] [1,L] [1,L]为白色, [ R , n ] [R,n] [R,n]为黑色,当 s − L < R − s s-L
特别地,当 s − L ≠ R − s s-L\neq R-s s−L=R−s,将颜色取反具有对称性,黑白数量相等。
当 s − L = R − s s-L=R-s s−L=R−s,由于黑色先手,当 [ 1 , L ] [1,L] [1,L]白( [ R , n ] [R,n] [R,n]黑),则 [ 1 , L ] [1,L] [1,L]白, [ L + 1 , n ] [L+1,n] [L+1,n]黑);当 [ 1 , L ] [1,L] [1,L]黑( [ R , n ] [R,n] [R,n]白), [ 1 , R − 1 ] [1,R-1] [1,R−1]黑, [ R , n ] [R,n] [R,n]白,这部分单独处理即可。
这部分枚举 s s s的位置,其方案数为 ∑ i = 1 s − i > 1 , s + i < n 2 2 i \sum\limits_{i=1}^{s-i>1,s+i
该式子可以通过前缀和计算,时间复杂度 O ( n ) O(n) O(n)。
#include<bits/stdc++.h>//写个代码验证答案
using namespace std;
const int N=2e5+5,mod=998244353;
typedef long long ll;
int n;
ll f1[N],f2[N],f3[N];
ll qpow(ll a,ll n)
{
ll ans=1;
for(;n;n>>=1,a=a*a%mod)
if(n&1) ans=ans*a%mod;
return ans;
}
int main()
{
scanf("%d",&n);
if(n==1) printf("%d\n",qpow(2,mod-2));
else
{
ll sum=qpow(2,n),isum=qpow(sum,mod-2);
for(int i=1;i<N;i++)
{
f1[i]=(f1[i-1]+qpow(2,2*i))%mod;
f2[i]=(f2[i-1]+qpow(2,2*i-1))%mod;
f3[i]=(f3[i-1]+i*qpow(2,2*i)%mod)%mod;
}
for(int s=1;s<=n;s++)
{
if(s==1||s==2||s==n-1||s==n)
{
printf("%lld\n",1ll*n*(mod-mod/2)%mod);
continue;
}
int k=min(s-2,n-s-1);//1<=i<=k
ll ans=1ll*(sum+mod-f1[k])%mod*n%mod*isum%mod*(mod-mod/2)%mod;
ans=(ans+(f2[k]*(n+1)%mod+f3[k])%mod*isum%mod)%mod;
printf("%lld\n",ans);
}
}
}
设直径长度为 d d d。
首先,若存在两条及以上的直径,由鸽巢原理必有直径的两个端点被染成同色,答案是 d ∗ 2 n d*2^n d∗2n。
否则,不妨令直径序列为 a 1 , a 2 , . . . , a k a_1,a_2,...,a_k a1,a2,...,ak,若 a 1 a_1 a1和 a k a_k ak同色,则答案是 d d d,否则只考虑 a 1 a_1 a1白色, a k a_k ak黑色的情况(最后答案乘 2 2 2即可):
1.若 a 2 a_2 a2为黑或 a k − 1 a_{k-1} ak−1为白,则答案一定是 d − 1 d-1 d−1。
2.若存在 ( a 2 , a k ) (a_2,a_k) (a2,ak)和 a 1 , a k − 1 a_1,a_{k-1} a1,ak−1之外其它长度为 d − 1 d-1 d−1的路径答案是 d − 1 d-1 d−1。
3.不存在其它路径且 a 2 a_2 a2染成白色, a k − 1 a_{k-1} ak−1染成黑色,接续考虑 a 3 , a k − 2 a_3,a_{k-2} a3,ak−2,依此类推,直到最后直径上剩余一个结点或不剩余结点为止。
加入 0 , n + 1 0,n+1 0,n+1两个位置,令 f ( l , r ) f(l,r) f(l,r)表示 [ l + 1 , r − 1 ] [l+1,r-1] [l+1,r−1]中首项 > l >l >l,末项 < r