1. 程式人生 > >洛谷聖誕賽

洛谷聖誕賽

waaadreamer的聖誕虐題賽

  雖然說聖誕節已經過去好多天了...

  等等,為什麼...全消失了!!!我本來寫完了...

  

  A.WD與矩陣

  題意概述:對於所有$n \times m$的01矩陣,有多少個每行每列異或值都是0.$T<=10^5,n,m<=10^9$

  設$f(n)$為填了$n$行,滿足條件的方案數,$g(n)$為填了$n$行,每行滿足條件,列不滿足條件的方案數.$f(1)=2^{m-1},g(1)=2^{m-1}$

  $f$下面填一行$0$還是$f$;$g$底下按照互補原則填一行也是$f$,不過這樣填上的一行是否合法呢?分析一下,$g$情況的全域性異或值是$0$,那麼有奇數個$1$的列就有偶數列,所以新填的一行一定滿足要求.那麼,其他的填法就都是轉移到$g$情況的了.

$$f(n)=f(n-1)+g(n-1),g(n)=(2^{m-1}-1)f(n)$$

  發現$f,g$永遠是加起來一起用的,所以可以換成一個變數$h(n)=(2^{m-1})h(n-1)$,快速冪就可以做了.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define mod 998244353
 4 # define ll long long 
 5 # define R register int
 6 
 7 using namespace std;
 8 
 9 int n,m,T;
10 11 ll qui (ll a,ll b) 12 { 13 ll s=1; 14 if(b==0) return a%mod; 15 while(b) 16 { 17 if(b&1) s=s*a%mod; 18 a=a*a%mod; 19 b=b>>1; 20 } 21 return s; 22 } 23 24 25 int read() 26 { 27 int x=0; 28 char c=getchar(); 29 while (!isdigit(c)) c=getchar();
30 while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); 31 return x; 32 } 33 34 int main() 35 { 36 scanf("%d",&T); 37 while(T--) 38 { 39 n=read(),m=read(); 40 if(n==1||m==1) printf("1\n"); 41 else printf("%lld\n",qui(qui(2,n-1),m-1)); 42 } 43 return 0; 44 }
A

 

  B.WD與迴圈

  題意概述:給出$n$,$m$,快速的求出$\sum_{i=1}^na_i<=m,a_i>=0$的方案數,$T<=10^5,n,m<=10^18$

  如果是求恰好等於$m$的方案數,可以用插板法,但是這裡還可以小於m,考慮一個轉化,設定一個虛擬的$a_{n+1}$,無論它是多少,最後直接捨棄,這樣就達到了小於等於$m$的目的.答案就是$C_{n+m}^m$,用$Lucas$求.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # define R register int
 4 # define ll long long
 5 
 6 using namespace std;
 7 
 8 const int maxn=19491010;
 9 int T;
10 ll maxx,n[100002],m[100002],f[maxn],inv[maxn];
11 int p=19491001;
12 
13 ll c(ll n,ll m)
14 {
15     if(n<m) return 0;  
16     return (f[n]*inv[m]%p)*inv[n-m]%p;
17 }
18 
19 ll lucas(ll n,ll m)
20 {
21     if(m==0)
22         return 1;
23     else
24         return 1LL*lucas(n/p,m/p)*c(n%p,m%p)%p;
25 }
26 
27 ll quick_pow(ll x,ll c)
28 {
29     ll s=1;
30     while (c)
31     {
32         if(c&1) s=s*x%p;
33         x=x*x%p;
34         c=c>>1;
35     }
36     return s%p;
37 }
38 
39 void init()
40 {
41     f[0]=1;
42     for (R i=1;i<=maxx;++i)    f[i]=f[i-1]*i%p;
43     inv[maxx]=quick_pow(f[maxx],p-2);
44     for (R i=maxx;i>=1;--i) inv[i-1]=inv[i]*i%p;
45 }
46 
47 ll read()
48 {
49     ll x=0;
50     char c=getchar();
51     while (!isdigit(c)) c=getchar();
52     while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
53     return x;
54 }
55 
56 int main()
57 {
58     scanf("%d",&T);
59     for (R i=1;i<=T;++i)
60         n[i]=read(),m[i]=read(),maxx=max(maxx,n[i]+m[i]);
61     if(maxx>=p-1) maxx=p-1;
62     init();
63     for (R i=1;i<=T;++i)
64         printf("%lld\n",lucas(n[i]+m[i],n[i]));
65     return 0;
66 }
B

 

  C.WD與數列

  題意概述:給定一個序列,求有多少個不相交的子序列滿足某個數列整體加上一個數後和另一個數列完全相同.$n<=3 \times 10^5$

  不會做...只會打一個$n<=1000$的部分分.

  首先對於原序列差分,轉化為有多少個不相交的子序列完全相同,注意到單個元素差分後會很混亂,所以只統計長度大於等於$2$的子序列,最後再將長度為$1$的貢獻加上.對於差分序列Hash,從右往左,開一個$map$統計左端點比當前指標位置靠右的區間的$Hash$值出現過幾次,列舉所有以當前指標為右端點的區間統計答案.注意一個小問題,差分陣列的相鄰位置還原後會成為三個數字,其中中間的一個是公用的,所以統計答案時不能統計右區間的左端點恰好處於左區間的右端點的下一個的情況.

  
 1 // luogu-judger-enable-o2
 2 # include <cstdio>
 3 # include <iostream>
 4 # include <map>
 5 # define R register int
 6 # define ll long long
 7 # define ULL unsigned long long
 8 
 9 using namespace std;
10 
11 const int maxn=1005;
12 const int base=2333;
13 int cnt,n;
14 ll a[maxn],las,x,ans;
15 ULL po[maxn],Hash[maxn];
16 map <ULL,int> m;
17 map <ll,int> k;
18 
19 ULL get_hash (int l,int r) { return Hash[r]-Hash[l-1]*po[r-l+1]; }
20 
21 int main()
22 {
23     scanf("%d",&n);
24     po[0]=1;
25     for (R i=1;i<=n;++i)
26         po[i]=po[i-1]*base;
27     for (R i=1;i<=n;++i)
28     {
29         scanf("%lld",&x);
30         a[i-1]=x-las;
31         las=x;
32     }
33     n--;
34     for (R i=1;i<=n;++i)
35     {
36         if(k[ a[i] ]!=0) a[i]=k[ a[i] ];
37         else k[ a[i] ]=cnt+1,a[i]=cnt+1,cnt+=1;
38     }
39     for (R i=1;i<=n;++i) Hash[i]=Hash[i-1]*base+a[i];
40     m[ get_hash(n,n) ]++;
41     for (R i=n-2;i>=1;--i)
42     {
43         for (R j=1;j<=i;++j)
44             ans+=m[ get_hash(j,i) ];
45         for (R j=i+1;j<=n;++j)
46             m[ get_hash(i+1,j) ]++;
47     }
48     printf("%lld",ans+n*(n+1)/2);
49     return 0;
50 }
C

 

  D.WD與積木

  題意好像說不大清楚.

  發現答案就是:  $$\frac{\sum_{i=1}^nS(n,i) \times i! \times i}{\sum_{i=1}^nS(n,i) \times i! }$$

  $S$是第二類斯特林數,我只會用$n^2$dp求...

  好像可以用一些神奇的多項式演算法求,優化到$NlogN$.

  
 1 // luogu-judger-enable-o2
 2 # include <cstdio>
 3 # include <iostream>
 4 # define mod 998244353
 5 # define ll long long
 6 # define R register int
 7 
 8 using namespace std;
 9 
10 const int maxn=1001;
11 int T,n;
12 int g[maxn][maxn],f[maxn];
13 ll ansu,ansd;
14 
15 ll qui (ll a,ll b)
16 {
17     ll s=1;
18     if(b==0) return a%mod;
19     while(b)
20     {
21         if(b&1) s=s*a%mod;
22         a=a*a%mod;
23         b=b>>1;
24     }
25     return s;
26 }
27 
28 int main()
29 {
30     scanf("%d",&T);
31     n=1000;
32     f[0]=1;
33     for (R i=1;i<=n;++i)
34         f[i]=1LL*f[i-1]*i%mod;
35     for (R i=0;i<=n;++i)
36         g[i][1]=g[i][i]=1;
37     g[0][0]=0;
38     for (R i=1;i<=n;++i)
39         for (R j=1;j<=i;++j)
40             g[i][j]=(g[i-1][j-1]+1LL*g[i-1][j]*j)%mod;
41     while(T--)
42     {
43         scanf("%d",&n);
44         ansu=ansd=0;
45         for (R i=1;i<=n;++i)
46             ansu=(ansu+1LL*g[n][i]*f[i]%mod*i)%mod;
47         for (R i=1;i<=n;++i)
48             ansd=(ansd+1LL*g[n][i]*f[i])%mod;
49         ansu=ansu*qui(ansd,mod-2)%mod;
50         printf("%lld\n",ansu);
51     }
52     return 0;
53 }
WD與積木