1. 程式人生 > >寒武紀camp Day3

寒武紀camp Day3

sqrt 向前走 同一行 比較 max 意義 情況 close !=

補題進度:9/10

A(多項式)

題意:

  在一個長度為n=262144的環上,一個人站在0點上,每一秒鐘有$\frac{1}{2}$的概率待在原地不動,有$\frac{1}{4}$的概率向前走一步,有$\frac{1}{4}$概率向後走一步,問t秒後這個人在x點的概率是多少,結果模998244353

分析:

  我們設:

    留在原地:$1$

    前進一步:$x$

    後退一步:$x^{-1}$

  那麽最後實際上是要求n=262144意義下$(\frac{x^{-1}}{4}+\frac{1}{2}+\frac{x}{4})^t$的第x項的系數

  因為n是p-1的因數,所以可以選n個單位根快速冪求值,然後NTT插值插出多項式即可。

  時間復雜度O(nlogn)

B(JAVA/模大質數)

C(LIS)

D(行列式+容斥)

題意:

  給出一個長度為n的數組a,其中$a_i<a_{i+1}$

  有n個機器人,第i個機器人在平面上的$(-a_i,a_i)$位置,每一秒鐘可以向上走一步或者向右走一步

  有n個終點,第i個終點在平面上的$(a_{n+i-i},m-a_{n+i-i})$位置,其中m是給定常數

  在同一秒中同一個位置不能有兩個機器人

  現在每個機器人都要到它對應的終點去,問有多少合法的不沖突的走路方案,答案模p

  1<=n<=100,1<=m<=1e18,2<=p<=1e9+7,0<=$a_i$<=1000

分析:

   我們發現兩個機器人在同一秒在同一個位置這與它們的路徑相交是等價的,於是問題就變成了求路徑互相不相交的方案數

  這個就利用16年多校的一個用行列式來容斥的題的套路差不多了,具體就是$a_{i,j}$表示第i個機器人走到第j個終點的方案數,然後求$det a$的值就是最後的結果了

  這裏註意到每個機器人走的步數一定都是m,那麽第i個機器人走到第j個終點的方案數就是C(m,a[i]+a[n-j+1])

  於是我們要想辦法快速求出C(m,0),C(m,1),....,C(m,2000)

  有一個$O(2000^2 * log(m))$的倍增方法來計算它

  具體就是C(2m,i)=C(m,0)*C(m,i)+C(m,1)*C(m,i-1)+...+C(m,i)*C(m,0),即要從2m個數中挑i個,我可以將2m個數分成左右兩部分,左右兩部分各自挑,總和是i就行了

  然後因為是模非質數,所以求行列式就不能直接求逆了,可以用輾轉相減法求行列式

技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=2000;
 4 int c[maxn+5],x[maxn+5];
 5 int a[maxn+5][maxn+5];
 6 int n,mod;
 7 long long m,ans;
 8 void cal(long long m)
 9 {
10     if(m==0)
11     {
12         c[0]=1;
13         return;
14     }
15     if(m&1)
16     {
17         cal(m-1);
18         for(int i=maxn;i>0;--i) c[i]=(c[i]+c[i-1])%mod;
19     }
20     else
21     {
22         cal(m/2);
23         for(int i=maxn;i>0;--i)
24             for(int j=0;j<i;++j)
25                 c[i]=(c[i]+1LL*c[j]*c[i-j])%mod;
26     }
27 }
28 int main()
29 {
30     scanf("%d%lld%d",&n,&m,&mod);
31     cal(m);
32     for(int i=1;i<=n;++i) scanf("%d",&x[i]);
33     for(int i=1;i<=n;++i)
34         for(int j=1;j<=n;++j)
35             a[i][j]=c[x[i]+x[n-j+1]];
36     ans=1;
37     cout<<clock()<<endl;
38     for(int i=1;i<=n;++i)
39         for(int j=i+1;j<=n;++j)
40             while(a[j][i])
41             {
42                 int d=a[i][i]/a[j][i];
43                 for(int k=1;k<=n;++k)
44                 {
45                     a[i][k]=(a[i][k]-1LL*d*a[j][k]%mod+mod)%mod;
46                     swap(a[i][k],a[j][k]);
47                 }
48                 ans=-ans;
49             }
50     ans=(ans+mod)%mod;
51     for(int i=1;i<=n;++i) ans=ans*a[i][i]%mod;
52     printf("%lld\n",ans);
53     return 0;
54 }
View Code

E(線段樹)

題意:

  有一個長度為n的數組a,要支持兩個操作:

  0 l r x :在[l,r]中的$\frac{(r-l+1)(r-l+2)}{2}$個子區間中等概率隨機選一個,給其中的所有數加上x

  1 l r :在[l,r]中的$\frac{(r-l+1)(r-l+2)}{2}$個子區間中等概率隨機選一個,問這個子區間的和的期望是多少

  答案對998244353求模

  1<=n<=100000,1<=m<=100000

分析:

  可以推一波式子,發現加操作相當於給一段區間加上一個二次函數$ax^2+bx+c$,詢問相當於問一段區間每個位置乘上一個二次函數之後的和

  我們可以用線段樹維護一段區間[l,r]的Σa[i]、Σi*a[i]、Σi^2a[i]就能把每個詢問拆成三部分然後累加求和了

  對於修改我們打lazy就行了

F(AC自動機+容斥+矩乘)

題意:

  給出n個字符串,問長度為l的字符串有多少個是包含了這n個字符串作為子串的。

  0<=l<=1e9,1<=n<=8,每個串長度不超過6

分析:

  如果l比較小,那就建個AC自動機,然後在上面跑狀壓DP就行了,dp[i][j][s]表示跑了i步在AC自動機的j點,字符串包含情況是S的情況下的方案數

  但現在問題是l很大,考慮把第一維用矩陣乘法代替

  那麽很容易想到建一個k=48*256的轉移矩陣,然後矩陣快速冪

  這是可以在O(klogk logl)的時間內完成的(用FFT優化O(k^2logl)的算法),但不是很好寫而且常數巨大

  這題n比較小可以考慮把這些字符串容斥,我們每次求出“至少不經過這樣幾個串的方案數”,就是1<<n次k=48的矩陣乘法,用O(k^3logl)的算法即可

G(FWT)

題意:

  FWT

分析:

  FWT真板子題

H(遞推數列循環節)

待填坑

I(圖論構造)

J(polya計數)

題意:

  你需要給n*m矩陣的每個格子填上0/1

  矩陣循環平移之後如果相同則視為相同

  矩陣每一行的異或和、每一列的異或和必須是0

  你需要求出有多少種本質不同的染色方案

  n,m<=1e9

分析:

  明顯的二維polya計數,如果沒有“同行同列異或和為0”這一限制,那答案明顯就是技術分享圖片

  其中a和b分別是行/列的循環節長度,即n/a,m/b分別是行、列的平移量,我們來討論該種置換下不動點的個數

  原本自由變元的數目是nm/lcm(a,b),現在有了“同行同列異或和為0”這一限制,我們需要犧牲一些自由變元

  我們首先來考慮行限制,行限制應該是類似下面這種的:

  1 xor 2 xor 3 xor 1 xor 2 xor 3 xor 1 xor 2 xor 3 ...... =0

  4 xor 5 xor 6 xor 4 xor 5 xor 6 xor 4 xor 5 xor 6 .......=0

  .....

  那麽本質不同的行限制方程明顯有n/a個,即行循環的步長,那麽這n/a個方程是不是都有效呢?

  我們還要考慮同一行中所有數字的重復次數,如果是奇數,那麽這個方程是有效的,如果是偶數,那麽是恒等於0的,是無效的

  那麽循環次數是多少呢?

  一個數字再矩陣中一共出現l次,在同一列出現了a次,所以一共會在l/a列出現,即同一行每個數字會出現l/a次,我們只需要判斷l/a的奇偶性就可以了

  對於列限制我們同理去考慮

  然後拿剛開始的自由元去減去有效方程的個數,就是真正自由元的數目

  註意,如果l/a l/b都是奇數,那麽這兩組方程會導出一個無效方程(就是把所有的行限制異或起來和把所有列限制異或起來會得到矩陣的整體異或方程),所以自由元需要減去1

  註意實現精妙一些就行,我們可以先把n,m質因數分解,然後dfs去構造他們的因數,因為這樣比較方便在構造過程中求出每個的歐拉函數

技術分享圖片
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define mp make_pair
 4 const int mod=1e9+7;
 5 vector<pair<int,int> > A,B,a,b;
 6 int n,m;
 7 void prime(vector<pair<int,int> > &a,int x)
 8 {
 9     int limit=sqrt(x+0.5);
10     for(int i=2;i<=limit;++i)
11         if(x%i==0)
12         {
13             int s=0;
14             while(x%i==0) x/=i,++s;
15             a.push_back(mp(i,s));
16         }
17     if(x!=1) a.push_back(mp(x,1));
18 }
19 void dfs(vector<pair<int,int> > &A,vector<pair<int,int> > a,int k,int factor,int fai)
20 {
21     if(k==a.size()) A.push_back(mp(factor,fai));
22     else
23     {
24         for(int i=0;i<=a[k].second;++i)
25         {
26             dfs(A,a,k+1,factor,fai);
27             factor=factor*a[k].first;
28             if(i==0) fai=fai*(a[k].first-1);else fai=fai*a[k].first;
29         }
30     }
31 }
32 long long Pow(long long a,long long b)
33 {
34     long long ans=1;
35     while(b)
36     {
37         if(b&1) ans=ans*a%mod;
38         a=a*a%mod;
39         b>>=1;
40     }
41     return ans;
42 }
43 int main()
44 {
45     int T;
46     scanf("%d",&T);
47     while(T--)
48     {
49         scanf("%d%d",&n,&m);
50         a.clear(),b.clear(),A.clear(),B.clear();
51         prime(a,n);
52         prime(b,m);
53         dfs(A,a,0,1,1);
54         dfs(B,b,0,1,1);
55         long long sum=0;
56         for(int i=0;i<A.size();++i)
57             for(int j=0;j<B.size();++j)
58             {
59                 long long ans=1;
60                 ans=ans*A[i].second%mod*B[j].second%mod;
61                 long long lcm=1LL*A[i].first/__gcd(A[i].first,B[j].first)*B[j].first;
62                 long long num=1LL*n*m/lcm;
63                 if(lcm/A[i].first%2==1&&lcm/B[j].first%2==1) num-=n/A[i].first+m/B[j].first-1;
64                 else
65                     if(lcm/A[i].first%2==1) num-=n/A[i].first;
66                     else
67                         if(lcm/B[j].first%2==1) num-=m/B[j].first;
68                 ans=ans*Pow(2LL,num)%mod;
69                 sum=(sum+ans)%mod;
70             }
71         printf("%lld\n",sum*Pow(1LL*n,1LL*mod-2)%mod*Pow(1LL*m,1LL*mod-2)%mod);
72     }
73     return 0;
74 }
View Code

寒武紀camp Day3