[考試反思]0518省選模擬99:扭曲

不知道該說啥。

線段樹判斷條件

$r<=r$

反正把自己氣得不輕。$70$的暴力已經會寫了最後想寫正解然後爆零了。。。

$T3$數據奇水,模擬直接$AC$(後來加了一組數據然而依然是隨便亂搞就能過)

不重要反正自己沒信仰沒寫模擬。。。也當是長記性了。。。

$T2$寫的純暴力卡常騙得分倒是也不少。然而並沒有什麼卵用。

$T2$真的是好題。。。重學$FWT$了。。。

 

T1:矩陣求和

大意:$n \times m$矩陣,$(i,j)$上寫着$(i-1)\times m+j$。支持:交換兩行,交換兩列,對一個矩形做$k$次二維前綴和之後所有值的和。$k \le 10,n,m,q \le 10^5$

首先這樣一個矩陣可以分開行列討論。每個位置的值就是$A_i \times  i + B_j \times j$。

行列交換就是交換兩個$A_i,A_j$或交換兩個$B_i,B_j$。

求答案的時候也只用分開考慮$A,B$的貢獻就可以了。問題下降到一維。

前綴和啥的還是可以組合數解決,答案形式如下:$\binom{r-l+k+1}{k+1} \sum A_i \binom{d-i+k}{k} $

把後面那個組合數用斯特林數展開。得到一長串式子,至於$A_i \times (-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\n",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 }
View Code

 

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 = \sum\limits_{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 \times 2^m$的矩陣,這個大矩陣裏一共也就$2^{k-1}$種值。

我們不在意這個,我們只關注每一列對位相乘之後的結果。那麼也就是$\prod (\sum\limits_{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} - ...  = \sum\limits_{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 }
View Code

 

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 }
哈希表代碼(最大化solve返回值)

胖頭魚系列出題人還是比較良心的。除了數據有點水(也可能是校內自造的)然後$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\n",ans);
63 }
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章