洛谷圣诞赛

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与积木

猜你喜欢

转载自www.cnblogs.com/shzr/p/10201414.html