不知道该说啥。
线段树判断条件
$r<=r$
反正把自己气得不轻。$70$的暴力已经会写了最后想写正解然后爆零了。。。
$T3$数据奇水,模拟直接$AC$(后来加了一组数据然而依然是随便乱搞就能过)
不重要反正自己没信仰没写模拟。。。也当是长记性了。。。
$T2$写的纯暴力卡常骗得分倒是也不少。然而并没有什么卵用。
$T2$真的是好题。。。重学$FWT$了。。。
T1:矩阵求和
大意:$n imes m$矩阵,$(i,j)$上写着$(i-1) imes m+j$。支持:交换两行,交换两列,对一个矩形做$k$次二维前缀和之后所有值的和。$k le 10,n,m,q le 10^5$
首先这样一个矩阵可以分开行列讨论。每个位置的值就是$A_i imes i + B_j imes j$。
行列交换就是交换两个$A_i,A_j$或交换两个$B_i,B_j$。
求答案的时候也只用分开考虑$A,B$的贡献就可以了。问题下降到一维。
前缀和啥的还是可以组合数解决,答案形式如下:$inom{r-l+k+1}{k+1} sum A_i inom{d-i+k}{k} $
把后面那个组合数用斯特林数展开。得到一长串式子,至于$A_i imes (-i)^x$有关。对于所有$x le k$都线段树维护出来就好了。
单点修改区间查询。总时间复杂度$O(qklogn+qk^2)$
1 #include<cstdio> 2 const int mod=1000000007,S=100055; 3 int mo(int x){return x>=mod?x-mod:x;} 4 int n,m,q,A[S],B[S],C[S][12],st[12][12],pw[S][12],inv[12],Q[11]; char s[5]; 5 struct T{int w[12];}aT[S<<2],bT[S<<2]; 6 #define md (L+R>>1) 7 #define lc p<<1 8 #define rc lc|1 9 void build(T*t,int v,int x,int p,int L,int R){ 10 if(L==R){ 11 t[p].w[0]=v; 12 for(int i=1;i<11;++i)t[p].w[i]=t[p].w[i-1]*(0ll+mod-L)%mod; 13 return; 14 }if(x<=md)build(t,v,x,lc,L,md);else build(t,v,x,rc,md+1,R); 15 for(int i=0;i<11;++i)t[p].w[i]=mo(t[lc].w[i]+t[rc].w[i]); 16 } 17 void geT(T*t,int l,int r,int p,int L,int R){ 18 if(l<=L&&R<=r){for(int i=0;i<11;++i)Q[i]=mo(Q[i]+t[p].w[i]);return;} 19 if(l<=md)geT(t,l,r,lc,L,md); if(r>md)geT(t,l,r,rc,md+1,R); 20 } 21 int main(){ 22 scanf("%d%d%d",&n,&m,&q); 23 for(int i=2;i<=n;++i)A[i]=mo(A[i-1]+m),build(aT,A[i],i,1,1,n); 24 for(int i=1;i<=m;++i)B[i]=i,build(bT,i,i,1,1,m); 25 for(int i=0;i<S;++i)for(int j=C[i][0]=1;j<=i&&j<12;++j)C[i][j]=mo(C[i-1][j-1]+C[i-1][j]); 26 for(int i=0;i<S;++i)for(int j=pw[i][0]=1;j<12;++j)pw[i][j]=pw[i][j-1]*1ll*i%mod; 27 for(int i=st[0][0]=1;i<12;++i)for(int j=1;j<12;++j)st[i][j]=(st[i-1][j]*(i-1ll)+st[i-1][j-1])%mod; 28 inv[0]=inv[1]=1; 29 for(int i=2;i<12;++i)inv[i]=mod-mod/i*1ll*inv[mod%i]%mod; 30 for(int i=2;i<12;++i)inv[i]=1ll*inv[i-1]*inv[i]%mod; 31 for(int _=0,l,r,u,d,k,ans;_<q;++_){ 32 scanf("%s%d%d",s,&u,&l); 33 if(s[0]=='Q'){ 34 scanf("%d%d%d",&d,&r,&k); ans=0; 35 for(int i=0;i<11;++i)Q[i]=0; 36 geT(aT,u,d,1,1,n); 37 for(int s=0;s<=k;++s)for(int j=s;j<=k;++j)ans=(ans+1ll*pw[d+k][s]*st[k][j]%mod*(k-j&1?mod-1:1)%mod*C[j][s]%mod*Q[j-s]%mod*inv[k]%mod*C[r-l+k+1][k+1])%mod; 38 for(int i=0;i<11;++i)Q[i]=0; 39 geT(bT,l,r,1,1,m); 40 for(int s=0;s<=k;++s)for(int j=s;j<=k;++j)ans=(ans+1ll*pw[r+k][s]*st[k][j]%mod*(k-j&1?mod-1:1)%mod*C[j][s]%mod*Q[j-s]%mod*inv[k]%mod*C[d-u+k+1][k+1])%mod; 41 printf("%d ",ans); 42 }else if(s[0]=='R')A[u]^=A[l]^=A[u]^=A[l],build(aT,A[u],u,1,1,n),build(aT,A[l],l,1,1,n); 43 else B[u]^=B[l]^=B[u]^=B[l],build(bT,B[u],u,1,1,m),build(bT,B[l],l,1,1,m); 44 } 45 }
T2:西行寺无余涅槃
大意:有$k$类菜,每类有$a_i$种。有$n$天。第$i$天吃第$j$类菜有$p_{i,j}$愉悦度(可以有很多天都同一种菜)。总愉悦度是每天愉悦度的异或和。
$p_{i,j} <2^m$。求对于$forall i <2^m$,$n$天下来愉悦度恰好是这个值的方案数。$m+k le 20,n le 10^6,k le 10$
注意下边菜的编号范围是$[0,k)$
不难想到暴力,直接对于每一天搞一个生成函数然后暴力$xorFWT$,对位累乘然后再IDFT$回来。
还有一个微小的优化,我们把所有的$p_{i,j}$都异或上$p_{i,0}$最后求答案时再异或回来,这样可以让$k$的范围缩小$1$
这里生成函数很特殊,系数表达中,只有$a_0,a_1,a_2...a_{k-1}$这$k$种值。
考虑$xorFWT$的本质$FWT(A)_i = sumlimits_{j=0}^{len-1} (-1)^{popcnt(i & j)} A_j$
也就是说每个数的贡献系数要么是$1$要么是$-1$。因为我们把所有数都异或$p_{i,0}$了,所以$p_{i,0}$现在就是$0$
也就是说$a_0$的贡献系数一定是$1$,剩下的贡献系数是$1$或$-1$。一共$2^{k-1}$种取值。
我们把$n$个多项式点值写成一个$n imes 2^m$的矩阵,这个大矩阵里一共也就$2^{k-1}$种值。
我们不在意这个,我们只关注每一列对位相乘之后的结果。那么也就是$prod (sumlimits_{j=0}^{k} (-1)^{y_j} a_j)^{x_i}$
也就是,对于$2^m$列中的每一列$i$,我们也只在意$2^{k-1}$种取值$j$的出现次数$x_{i,j}$。然后就可以做累乘了。
首先,我们有$x_{i,0}+x_{i,1}+...+x_{i,2^{k-1}-1}=n$。原因比较显然。
然后,考虑$FWT$的本质。我们新建一个都为$0$的多项式$A$,对于所有$y in [1,n]$,都执行$A_{p_{y,1}}++$
对这个东西进行$xorFWT$会得到什么?根据$xorFWT$本质的那个式子,我们知道:
$FWT(A)_i= x_{i,0} - x_{i,1} + x_{i,2} - ... = sumlimits_{z=0}^{2^{k-1}-1} (-1)^{popcnt(z & 1)} x_{i,z}$
同理可以每次使得$A_{p_{y,2}}++$,或者使$A_{p_{y,1} xor p_{y,2}}++$。每一次都能列出$2^m$个方程。
这样,对于每一列$i$,我们都可以得到$2^{k-1}$个方程。问题在于解方程。
$ x_{i,0}+x_{i,1}+x_{i,2}+x_{i,3}+...=n \ x_{i,0}-x_{i,1}+x_{i,2}-x_{i,3}+...=FWT(A_1)_i \ x_{i,0}+x_{i,1}-x_{i,2}-x_{i,3}+...=FWT(A_2)_i \ x_{i,0}-x_{i,1}-x_{i,2}+x_{i,3}+...=FWT(A_3)_i \ ... $
可以发现左边这一摊就是一个非常经典的$xorFWT$形式。所以对右边的东西构造多项式然后IFWT$就可以得到所有$x_i$
时间复杂度$O(2^{k-1}k(n+2^m))$。然而实际运行很快。
1 #include<cstdio> 2 char s[100000000],*I=s; 3 void In(int&x){x=0;while(*I<48||*I>59)I++;while(*I>47&&*I<58)x=x*10+*I++-48;} 4 const int mod=998244353; 5 int len,p[1000005][11],n,m,k,T,A[1048576],B[1048576],*b=B,c[11],C[2333],ans[1048576]; 6 int qp(int b,int t,int a=1){for(;t;t>>=1,b=1ll*b*b%mod)if(t&1)a=1ll*a*b%mod;return a;} 7 int mo(int x){return x>=mod?x-mod:x;} 8 void FWT(int*a,int op=0){ 9 for(int i=1;i<len;i<<=1)for(int j=0;j<len;j+=i<<1)for(int k=j,x,y;k<j+i;++k) 10 x=a[k],y=a[k+i],a[k]=mo(x+y),a[k+i]=mo(mod+x-y); 11 if(op)for(int i=0,iv=qp(len,mod-2);i<len;++i)a[i]=a[i]*1ll*iv%mod; 12 } 13 int main(){ 14 fread(s,1,100000000,stdin); In(n);In(m);In(k); 15 for(int i=0;i<k;++i)In(c[i]); 16 for(int i=0;i<1<<k-1;++i)for(int j=0;j<k;++j)if(i&1<<j-1)C[i]=mo(C[i]-c[j]+mod);else C[i]=mo(C[i]+c[j]); 17 for(int i=1;i<=n;++i)for(int j=0;j<k;++j)In(p[i][j]); 18 for(int i=1;i<=n;++i)for(int j=1;j<k;++j)p[i][j]^=p[i][0]; 19 for(int i=1;i<=n;++i)T^=p[i][0]; 20 len=1<<m; 21 for(int S=1;S<1<<k-1;++S){ 22 for(int i=1,x;x=0,i<=n;++i){ 23 for(int j=1;j<k;++j)if(S>>j-1&1)x^=p[i][j]; 24 A[x]++; 25 }FWT(A); 26 for(int i=0;i<1<<m;++i)B[i<<k-1|S]=A[i],A[i]=0; 27 } 28 len=1<<k-1; 29 for(int S=0;S<1<<m;++S){ 30 b[0]=n;FWT(b,1); ans[S]=1; 31 for(int i=0;i<1<<k-1;++i)ans[S]=1ll*ans[S]*qp(C[i],b[i])%mod; 32 b+=len; 33 }len=1<<m;FWT(ans,1); 34 for(int i=0;i<1<<m;++i)printf("%d ",ans[i^T]); 35 }
T3:鱼贯而入
大意:问把$n$个$a_i$插入哈希表,自取$len ge n$。最大撞上的次数是多少。$n le 200,A_i le 10^{18}$
1 void add_fish(long long &cnt, long long x, long long len){ 2 long long y = x % len; 3 while(h[y] != -1 && h[y] != x) 4 y = (y + 1) % len, cnt++; 5 h[y] = x; 6 } 7 long long solve(long long len){ 8 for (int i=0; i< len; i ++) h[i]=-1 9 long long cnt = 0; 10 for (int i = 1; i <= n; i ++) add_fish(cnt, a[i], len); 11 return cnt; 12 }
胖头鱼系列出题人还是比较良心的。除了数据有点水(也可能是校内自造的)然后$std$有点锅(应该不是校内自造的)
首先,为了让有鱼撞上,你一定会选择某个$|A_i - A_j|$的因子作为$len$。
发现选择合数一定不优,所以我们会选择质数。但是如果质数小于$n$那又不合法。
所以我们的决策是:所有$>n^2$的质数,以及所有$in [n,n^2]$的数。
对于$>n^2$的合数,如果可以分割出一个小于$n$的质数就可以递归考虑。
如果分割不出的话那么所有质因子都大于$n$,也会在质因子除被考虑。
写个$Pollard-Rho$分解质因数。然后$check$的话暴力$check$就行。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 unordered_set<ll>S; 5 int n,r[222],ans;ll a[222]; 6 ll mo(ll x,ll m){return x>=m?x-m:x;} 7 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} 8 ll mul(ll x,ll y,ll m){return x<=2000000000&&y<=2000000000?x*y%m:mo((x*y-(ll)(1.L*x/m*y)*m)%m+m,m);} 9 ll qp(ll b,ll t,ll m,ll a=1){for(;t;t>>=1,b=mul(b,b,m))if(t&1)a=mul(a,b,m);return a;} 10 bool MR(ll x,int b){ ll k=x-1,y; 11 while(!(k&1)){ 12 if((y=qp(b,k,x))!=1)return y==x-1; 13 k>>=1; 14 }return 1; 15 } 16 bool pr(ll x){ 17 if(x==2||x==3||x==5||x==7||x==11||x==13||x==61)return 1; 18 if(x==1||x%2==0||x%3==0||x%5==0||x%7==0||x%11==0||x%13==0)return 0; 19 return MR(x,2)&&MR(x,11)&&MR(x,61); 20 } 21 ll PR(ll x){ 22 ll s=0,t=0,c=rand()%(x-1)+1,p=1,d; 23 for(int g=1;;g<<=1,s=t,p=1){ 24 for(int _=1;_<=g;++_){ 25 t=mo(mul(t,t,x)+c,x); p=mul(p,abs(t-s),x); 26 if(_%127==0){d=gcd(x,p);if(d>1)return d;} 27 }d=gcd(x,p);if(d>1)return d; 28 } 29 } 30 void Div(ll x){ 31 if(x<n)return; 32 if(pr(x)){S.insert(x);return;} 33 ll y=PR(x);Div(y);Div(x/y); 34 } 35 const int m=10000003;bool al[m];ll usd[222]; 36 struct hash_map{ 37 int fir[m],l[233],ec; ll to[233]; 38 bool f(ll x){int r=x%m; 39 for(int i=fir[r];i;i=l[i])if(to[i]==x)return 1; 40 l[++ec]=fir[r];fir[r]=ec;to[ec]=x;return 0; 41 }void clear(){while(ec)fir[to[ec--]%m]=0;} 42 }M; 43 int chk(ll d,int A=0){ 44 if(d<=m){ 45 for(int i=1;i<=n;++i){ 46 int x=a[i]%d; while(al[x])x=x==d-1?0:x+1,A++; 47 al[x]=1; usd[i]=x; 48 }for(int i=1;i<=n;++i)al[usd[i]]=0; 49 }else{ 50 for(int i=1;i<=n;++i){ 51 ll x=a[i]%d; 52 while(M.f(x))x=x==d-1?0:x+1,A++; 53 }M.clear(); 54 }return A; 55 } 56 int main(){ 57 scanf("%*d%d",&n); 58 for(int i=1;i<=n;++i)scanf("%lld",&a[i]); 59 for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)Div(abs(a[i]-a[j])); 60 for(int i=n;i<=n*n;++i)S.insert(i); 61 for(auto x:S)ans=max(ans,chk(x)); 62 printf("%d ",ans); 63 }