首页 > 技术文章 > [ICPC2022Macau]Pass the Ball!【双模术NTT】【分块】

Menhera 2022-04-07 23:27 原文

分析:看到$\sum i*b_i$的式子以及每次传球向右移动一位,很容易想到HNOI2017项链的循环卷积。假设我们用FFT处理出来了每个环传0次,传1次,传2次,...,传环的大小n-1次对应的结果。那么对于每个询问k,只需要对于每个环,找出k模环的大小对应的答案即可。但是如果对每个环都找,时间复杂度是吃不消的。所以采用分块的方法,对于小于$\sqrt(n)$的环,把答案加进一个预处理数组中,对于大于$\sqrt(n)$的环,直接枚举所有询问,把结果加进去。最后再对每个询问枚举小于$\sqrt(n)$的环的累计答案。

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int maxn = 102000;
  5 const int mod1 = 998244353;
  6 const int mod2 = 1004535809;
  7 
  8 int n,m,num;
  9 int p[maxn],vis[maxn],query[maxn];
 10 vector<int> vec;
 11 vector<int> ans1[500]; // xiaoyu sqrtn de daan
 12 vector<int> ans2[500];
 13 
 14 int res1[maxn],res2[maxn]; //zuizhongdaan
 15 
 16 int A[maxn*16],B[maxn*16],C[maxn*16];
 17 int r[maxn*16],len = 1,bit=0;
 18 void build_P(){
 19     for(int i=0;i<9*num;i++) A[i] = B[i] = C[i] = 0;
 20     for(int i=0;i<num;i++) A[num-i-1] = vec[i];
 21     for(int i=0;i<num;i++) B[i] = B[num+i] = vec[i];
 22 }
 23 
 24 int fast_pow(int dt,int pw,int mod){
 25     int res=1,now=dt,p=1;
 26     while(p<=pw){
 27     if(p&pw) res = 1ll*res*now%mod;
 28     now=1ll*now*now%mod;
 29     p<<=1;
 30     }
 31     return res;
 32 }
 33 
 34 void FFT(int *a,int flag,int mod){
 35     for(int i=0;i<len;i++) if(i < r[i]) swap(a[i],a[r[i]]);
 36     for(int mid = 1;mid<len;mid<<=1){
 37     int nw = fast_pow(3,(mod-1)/(2*mid),mod);
 38     if(flag == -1) nw = fast_pow(nw,mod-2,mod);
 39     for(int i=0;i<len;i+=(mid<<1)){
 40         int tmp = 1;
 41         for(int j=0;j<mid;j++,tmp=1ll*tmp*nw%mod){
 42         int x = a[i+j],y=1ll*tmp*a[i+j+mid]%mod;
 43         a[i+j]=(x+y)%mod,a[i+j+mid]=(x-y+mod)%mod;
 44         }
 45     }
 46     }
 47     if(flag == -1){
 48     int dt = fast_pow(len,mod-2,mod);
 49     for(int i=0;i<len;i++){
 50         a[i] = 1ll*a[i]*dt%mod;
 51     }
 52     }
 53 }
 54 
 55 void fft(int mod){
 56     len = 1,bit = 0;
 57     while(len < 4*num){len<<=1,bit++;}
 58     for(int i=0;i<len;i++) r[i] = (r[i>>1]>>1)|((i&1)<<(bit-1));
 59     FFT(A,1,mod); FFT(B,1,mod);
 60     for(int i=0;i<len;i++) C[i] = 1ll*A[i]*B[i]%mod;
 61     FFT(C,-1,mod);
 62 }
 63 
 64 long long xxx,yyy;
 65 void exgcd(int a,int b){
 66     if(!b){
 67     xxx=1;yyy=0;return;
 68     }
 69     exgcd(b,a%b);
 70     long long t = xxx; xxx = yyy; yyy = t-a/b*yyy;
 71 }
 72 
 73 long long fast_mul(long long dt,long long pw,long long mod){
 74     long long res=0,now=dt,p=1;
 75     while(p<=pw){
 76     if(p&pw) res = (res+now)%mod;
 77     now=(now+now)%mod;
 78     p<<=1;
 79     }
 80     return res;
 81 }
 82 
 83 long long merge(int b,int d){
 84     long long mod = 1ll*mod1*mod2;
 85     long long as;
 86     if(d-b > 0){
 87     as = fast_mul(fast_mul(xxx,(d-b),mod),mod1,mod)+b;
 88     as %= mod;
 89     as += mod; as%= mod;
 90     }else{
 91     long long xx = -xxx;
 92     as = fast_mul(fast_mul(xx,b-d,mod),mod1,mod)+b;
 93     as %= mod;
 94     as += mod; as%= mod;
 95     }
 96     return as;
 97 }
 98 
 99 int main(){
100     //freopen("1.in","r",stdin);
101     ios::sync_with_stdio(false);
102     cin >> n >> m;
103     for(int i=1;i<=n;i++){
104     cin >> p[i];
105     }
106     for(int i=1;i<=m;i++) cin >> query[i];
107     
108     for(int i=1;i*i<=n;i++){
109     for(int j=0;j<i;j++) ans1[i].push_back(0),ans2[i].push_back(0);
110     }
111     
112     for(int i=1;i<=n;i++){
113     if(!vis[i]){
114         int j = i;vec.clear();num = 0;
115         while(!vis[j]){num++;vec.push_back(j);vis[j]=1;j = p[j];}
116         build_P();
117         fft(mod1);
118         if(num*num <= n){
119         for(int j=0;j<num;j++){
120             ans1[num][j]+=C[num-1+j];
121             ans1[num][j]%=mod1;
122         }
123         }else{
124         for(int j=1;j<=m;j++){
125             int dt = query[j]%num;
126             res1[j] += C[num-1+dt];
127             res1[j]%=mod1;
128         }
129         }
130         build_P();
131         fft(mod2);
132         if(num*num <= n){
133         for(int j=0;j<num;j++){
134             ans2[num][j]+=C[num-1+j];
135             ans2[num][j]%=mod2;
136         }
137         }else{
138         for(int j=1;j<=m;j++){
139             int dt = query[j]%num;
140             res2[j] += C[num-1+dt];
141             res2[j]%=mod2;
142         }
143         }
144     }
145     }
146     for(int i=2;i*i<=n;i++){
147     for(int j=1;j<=m;j++){
148         res1[j] += ans1[i][query[j]%i];
149         res1[j]%=mod1;
150         res2[j] += ans2[i][query[j]%i];
151         res2[j]%=mod2;
152     }
153     }
154     exgcd(mod1,mod2);
155     for(int i=1;i<=m;i++){
156     //cout<<res1[i]<<" "<<res2[i]<<endl;
157     cout<<merge(res1[i],res2[i])<<endl;
158     }
159     return 0;
160 }

 

推荐阅读