A
显然走路策略不是一直向左就是一直向右,不是一直向下就是一直向上。
假如 a [ i ] ! = a [ i + 1 ] a[i]!=a[i+1] a[i]!=a[i+1],那么此时不管 b [ j ] b[j] b[j] 是什么,从 ( i , j ) (i,j) (i,j) 走到 ( i + 1 , j ) (i+1,j) (i+1,j) 时间必然都 + 1 +1 +1,所以可以发现,横向移动与竖向移动之间互不影响,所以我们求一个前缀和与后缀和,每次看看向左还是向右,向上还是向下就好了。
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define maxn 100010
int n,m,q,a[maxn],b[maxn];
int suma1[maxn],suma2[maxn];
int sumb1[maxn],sumb2[maxn];
int main()
{
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=m;i++)scanf("%d",&b[i]);
for(int i=2;i<=n;i++)suma1[i]=suma1[i-1]+(a[i]!=a[i-1]);
for(int i=2;i<=m;i++)sumb1[i]=sumb1[i-1]+(b[i]!=b[i-1]);
for(int i=n-1;i>=1;i--)suma2[i]=suma2[i+1]+(a[i]!=a[i+1]);
for(int i=m-1;i>=1;i--)sumb2[i]=sumb2[i+1]+(b[i]!=b[i+1]);
scanf("%d",&q);
while(q--)
{
int x,y,xx,yy,ans=0;
scanf("%d %d %d %d",&x,&y,&xx,&yy);
if(xx<x)swap(x,xx);if(yy<y)swap(y,yy);
if(x!=xx)ans+=min(suma1[xx]-suma1[x],suma1[x]+suma2[xx]+(a[1]!=a[n]));
if(y!=yy)ans+=min(sumb1[yy]-sumb1[y],sumb1[y]+sumb2[yy]+(b[1]!=b[m]));
printf("%d\n",ans);
}
}
B
看到这种敢随机给数据的,就容易想到珂朵莉树,不难发现,又是要我们进行复杂度分析了。
对于一个位置 i i i,假如存在 j j j 满足 i < j i<j i<j 且 a i < a j a_i<a_j ai<aj,那么显然 i i i 是对答案无影响的,如果一个点对答案有影响,那么显然满足 ∀ j > i : a i > a j \forall j>i:a_i>a_j ∀j>i:ai>aj。那么 i i i 有影响的概率为 1 n − i + 1 \frac 1 {n-i+1} n−i+11,于是可以得到,这个数列中有影响的点的期望个数为 ∑ i = 1 n 1 i = ln n ≈ log 2 n \sum_{i=1}^n \frac 1 i=\ln n \approx \log_2 n ∑i=1ni1=lnn≈log2n。
这样的话,每次询问我们找到这些有影响的点,更新一波答案即可。
维护的话选择用线段树,每个节点记录区间内的最大和最小值,这样修改操作很容易就能解决。
询问的话,设 c a l c ( x , y ) calc(x,y) calc(x,y) 表示将 x , y x,y x,y 代入式子 a x + b y + c x y ax+by+cxy ax+by+cxy 的值。在递归时,先看右儿子,设左右儿子的最大值分别为 m a x _ z u o , m a x _ y o u max\_zuo,max\_you max_zuo,max_you,假如满足 c a l c ( r , m a x _ y o u ) > a n s calc(r,max\_you)>ans calc(r,max_you)>ans,那么就递归进入右儿子,然后搞完右儿子再看左儿子,左儿子如法炮制,看是否满足 c a l c ( m i d , m a x _ z u o ) > a n s calc(mid,max\_zuo)>ans calc(mid,max_zuo)>ans 即可。
这样做的意义,其实就是在看这个儿子里面是否存在有影响的点,可以发现,我们每次优先处理右儿子,这样可以保证,在处理区间 x x x ~ y y y 时,右边的 y + 1 y+1 y+1 ~ n n n 都处理完了,此时的 a n s ans ans 记录的就是右边区间的最大值,假如存在 c a l c ( v a l u e _ m a x , y ) > a n s calc(value\_max,y)>ans calc(value_max,y)>ans,那么这个 v a l u e _ m a x value\_max value_max 一定比右边的所有 a i a_i ai 都大,那么就是有影响的。
代码如下:
#include <cstdio>
#include <algorithm>
using namespace std;
#define ll long long
int n,m,x0,v[100010];
int rd(){
x0=(100000005ll*x0+20150609ll)%998244353;return x0/100;}
ll ans,a,b,c;
ll calc(int x,int y){
return (ll)a*x+(ll)b*y+(ll)c*x*y;}
struct node{
int l,r,mid,ma,mi,lazy;
node *zuo,*you;
void check(){
ma=max(zuo->ma,you->ma);mi=min(zuo->mi,you->mi);}
node(int x,int y):l(x),r(y),lazy(0)
{
mid=l+r>>1;
if(l<r)
{
zuo=new node(l,mid);you=new node(mid+1,r);
check();
}
else ma=mi=v[l],zuo=you=NULL;
}
void update(){
lazy^=1;swap(mi,ma);mi=100000-mi;ma=100000-ma;}
void pushdown()
{
if(lazy)
{
if(zuo!=NULL)zuo->update(),you->update();
lazy=0;
}
}
void Cchange(int x,int y)
{
if(l==r){
ma=mi=y;return;}
pushdown();
if(x<=mid)zuo->Cchange(x,y);
else you->Cchange(x,y);
check();
}
void Rchange(int x,int y)
{
if(l==x&&r==y){
update();return;}
pushdown();
if(y<=mid)zuo->Rchange(x,y);
else if(x>=mid+1)you->Rchange(x,y);
else zuo->Rchange(x,mid),you->Rchange(mid+1,y);
check();
}
void ask(int x,int y)
{
pushdown();
if(x<=l&&r<=y)
{
if(l==r)return ans=max(ans,calc(l,mi)),void();
if(calc(r,you->ma)>ans)you->ask(x,y);
if(calc(mid,zuo->ma)>ans)zuo->ask(x,y);
return;
}
if(l==r)return;
if(y>=mid+1)you->ask(x,y);
if(x<=mid)zuo->ask(x,y);
}
};
node *root;
char s[5];
int main()
{
scanf("%d %d %d",&n,&m,&x0);
for(int i=1;i<=n;i++)v[i]=rd()%100001;
root=new node(1,n);
while(m--)
{
scanf("%s",s);
int x=rd(),y=rd();
if(s[0]=='C')root->Cchange(x%n+1,y%100001);
else
{
x=x%n+1;y=y%n+1;if(x>y)swap(x,y);
if(s[0]=='R')root->Rchange(x,y);
else
{
scanf("%lld %lld %lld",&a,&b,&c); ans=0ll;
root->ask(x,y); printf("%lld\n",ans);
}
}
}
}
C
将限制从 [ 1 , c i ] [1,c_i] [1,ci] 改成 [ 0 , c i ) [0,c_i) [0,ci),显然是不影响的,将所有合法多项式常数项 − 1 -1 −1 即可。
考虑这个多项式的下降幂形式,设为 f ( x ) = ∑ i = 0 n q i x i ‾ f(x)=\sum_{i=0}^n q_i x^{\underline i} f(x)=∑i=0nqixi。
对于第 i i i 个限制 0 ≤ f ( i ) < c i 0\leq f(i)<c_i 0≤f(i)<ci,可以写成 0 ≤ q i i i ‾ + . . . + q 0 i 0 ‾ < c i 0\leq q_ii^{\underline i}+...+q_0i^{\underline 0}<c_i 0≤qiii+...+q0i0<ci,将除了第 i i i 项以外的加起来记为 C C C,就有 − C ≤ q i i i ‾ < c i − C -C\leq q_ii^{\underline i}<c_i-C −C≤qiii<ci−C,于是 q i q_i qi 的方案数就是:
{ ⌊ c i i i ‾ ⌋ ( C m o d i i ‾ ≥ c i m o d i i ‾ ) ⌊ c i i i ‾ ⌋ + 1 ( C m o d i i ‾ < c i m o d i i ‾ ) \begin{cases} \lfloor \dfrac {c_i} {i^{\underline i}}\rfloor~~~~~~~~~~~~~(C\bmod {i^{\underline i}}\geq c_i \bmod {i^{\underline i}})\\ \lfloor \dfrac {c_i} {i^{\underline i}}\rfloor+1~~~~~~(C\bmod {i^{\underline i}}<c_i \bmod {i^{\underline i}})\\ \end{cases} ⎩⎨⎧⌊iici⌋ (Cmodii≥cimodii)⌊iici⌋+1 (Cmodii<cimodii)
于是我们需要知道 C m o d i i ‾ C \bmod i^{\underline i} Cmodii 的值,这等于 ∑ j = 0 j − 1 q j × i j ‾ m o d i i ‾ \sum_{j=0}^{j-1} q_j\times i^{\underline j} \bmod i^{\underline i} ∑j=0j−1qj×ijmodii。
所以我们需要前面的 q j q_j qj 取模后的值,考虑枚举 q i m o d ( n − i ) ! q_i\bmod (n-i)! qimod(n−i)! 的值,设为 r i r_i ri,那么 q i = ( n − i ) ! × t + r i q_i=(n-i)!\times t+r_i qi=(n−i)!×t+ri。
由于现在枚举的是 r i r_i ri,所以取模时不对 i i ‾ = i ! i^{\underline i}=i! ii=i! 取模,而是对 i ! ( n − i ) ! i!(n-i)! i!(n−i)! 取模,对于一个 q j × i j ‾ q_j\times i^{\underline j} qj×ij,即 ( n − j ) ! × t ′ × i j ‾ (n-j)!\times t'\times i^{\underline j} (n−j)!×t′×ij,它对 i ! ( n − i ) ! i!(n-i)! i!(n−i)! 取模后的结果等于 r j m o d i ! ( n − i ) ! r_j\bmod i!(n-i)! rjmodi!(n−i)!,因为 ( n − j ) ! × i j ‾ (n-j)!\times i^{\underline j} (n−j)!×ij 整除 ( n − i ) ! × i i ‾ (n-i)!\times i^{\underline i} (n−i)!×ii。
于是 C m o d i ! ( n − i ) ! = ( ∑ j = 0 i − 1 r j ) m o d i ! ( n − i ) ! C\bmod i!(n-i)!=(\sum_{j=0}^{i-1} r_j)\bmod i!(n-i)! Cmodi!(n−i)!=(∑j=0i−1rj)modi!(n−i)!,将它与 c i m o d i ! ( n − i ) ! c_i\bmod i!(n-i)! cimodi!(n−i)! 作对比,即可求出 q i q_i qi 的方案数,枚举出所有 r i r_i ri 后,将 q i q_i qi 的方案数相乘就是贡献,将所有贡献加起来就是答案,时间复杂度 O ( n × ∏ i = 1 n i ! ) O(n\times \prod_{i=1}^n i!) O(n×∏i=1ni!)。
考虑优化第一层枚举,即 r 0 = 0 r_0=0 r0=0 ~ n ! − 1 n!-1 n!−1,当 r 0 r_0 r0 增加时, C m o d i ! ( n − i ) ! C\bmod i!(n-i)! Cmodi!(n−i)! 在不断加一然后归零,当 C ≥ c i ( m o d i ! ( n − i ) ! ) C\geq c_i \pmod {i!(n-i)!} C≥ci(modi!(n−i)!) 时,贡献为 ⌊ c i i ! ( n − i ) ! ⌋ \lfloor \dfrac {c_i} {i!(n-i)!}\rfloor ⌊i!(n−i)!ci⌋,否则就要 + 1 +1 +1,也就是说,在 C C C 的一个变化周期内,只会有两个变化点,周期长度为 n ! n! n!,变化点总数为 2 n ! i ! ( n − i ) ! = 2 ( n i ) 2\dfrac {n!} {i!(n-i)!}=2\Large\binom n i 2i!(n−i)!n!=2(in),总数为 ∑ i = 1 n 2 ( n i ) = 2 n + 1 \sum_{i=1}^n 2\binom n i=2^{n+1} ∑i=1n2(in)=2n+1,相邻两个变化点之间,对答案的贡献是不变的。
于是我们不需要枚举第一层,枚举完其它层之后,求出所有变化点,然后将变化点排序,最后再枚举 r 0 r_0 r0 求解即可,这样的好处是,你不需要每次求出变化点后就枚举 r 0 r_0 r0,你可以积攒一下,比如说积攒个 50 50 50 次枚举,将它们的所有变化点合起来一起求解,用桶排可以去掉排序的 log \log log,时间复杂度为 O ( ( n − 1 ) ! . . . 1 ! × 2 n − 1 ) O((n-1)!...1!\times 2^{n-1}) O((n−1)!...1!×2n−1)。
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 10
#define mod 998244353
int T,n,c[maxn],ans;
int fac[maxn],w[maxn][maxn];
int k[maxn],k1[maxn],k2[maxn];
int val[1<<maxn+1];
int r[maxn],sr[maxn];
struct par{
int x,y;}sav[730][510];
int tot[730],Sr[310][maxn],cnt=0;
void add(int &x,int y){
x=(x+y>=mod?x+y-mod:x+y);}
void dec(int &x,int y){
x=(x-y<0?x-y+mod:x-y);}
int sta[210];
void calc_ans(){
for(int i=1;i<=cnt;i++){
sta[i]=0;for(int j=0;j<=n;j++){
if(Sr[i][j]<k2[j])sta[i]|=(1<<j);
}
}
int sum=0;
for(int i=1;i<=cnt;i++)add(sum,val[sta[i]]);
add(ans,sum);
for(int i=1;i<fac[n];i++){
for(;tot[i];tot[i]--){
par p=sav[i][tot[i]];
dec(sum,val[sta[p.x]]);//在变化点改变贡献
add(sum,val[sta[p.x]^=p.y]);
}
add(ans,sum);
}
tot[0]=cnt=0;
}
void calc(){
cnt++;
for(int x=0;x<=n;x++){
for(int i=-sr[x];i<fac[n];i+=k[x])//找到所有变化点,i在枚举会产生变化的r0的值
if(i>=0)sav[i][++tot[i]]=(par){
cnt,1<<x};
for(int i=k2[x]-sr[x];i<fac[n];i+=k[x])
if(i>=0)sav[i][++tot[i]]=(par){
cnt,1<<x};
Sr[cnt][x]=sr[x];
}
if(cnt==50)calc_ans();
}
void dfs(int x){
if(x>n)return calc();
sr[x]=0;
for(int i=1;i<x;i++)sr[x]+=r[i]*w[x][i];//求C
sr[x]%=k[x];
for(r[x]=0;r[x]<fac[n-x];r[x]++)
dfs(x+1),sr[x]=(sr[x]+fac[x])%k[x];
}
int main()
{
fac[0]=1;for(int i=1;i<=6;i++)fac[i]=fac[i-1]*i;
w[0][0]=1;for(int i=1;i<=6;i++){
//下降幂预处理
w[i][0]=1;for(int j=1;j<=i;j++){
w[i][j]=w[i][j-1]*(i-j+1);
}
}
scanf("%d",&T);while(T--)
{
scanf("%d",&n);ans=0;
for(int i=0;i<=n;i++){
scanf("%d",&c[i]);
k[i]=fac[i]*fac[n-i];//第i层的模数
k1[i]=c[i]/k[i];k2[i]=c[i]%k[i];
}
for(int i=0;i<(1<<(n+1));i++){
//枚举每个位置是C%k[i]>=c[i]%k[i]还是<,对于每种情况求出贡献,一共2^{n+1}种
val[i]=1;for(int j=0;j<=n;j++){
val[i]=1ll*val[i]*(k1[j]+(i>>j&1))%mod;
}
}
dfs(1);calc_ans();
printf("%d\n",ans);
}
}