退役后的做题记录
...
我承认这个标题很奇怪...
总的来说呢,就是看看有什么好玩的题就做一做。
计数器:10;
2019.5.27:今天改了改emacs配置和桌面背景:https://www.cnblogs.com/shzr/p/10872530.html
1.染色问题:https://www.lydsy.com/JudgeOnline/problem.php?id=4487
题意概述:有一个 $n \times m$ 的棋盘,$c$ 种颜色,每个格子可以涂色,也可以空着。请问,同时满足以下要求的棋盘有多少种?
1.每列至少有一个格子涂色了; 2.每行至少有一个格子涂色了; 3.每种颜色至少用一次; $n,m,c<=400$
如果这些条件单独出现,那么显然是很好做的,只要容斥一下就好了。其实三个条件也没什么,对于每个条件依次容斥即可;
一个显然的事实是,如果没有上述限制,那么答案就是$(c+1)^{nm}$,所以容斥的式子也就可以很简单地写出来了:
$$\sum\limits_{i=0}^n\sum\limits_{j=0}^m\sum\limits_{k=0}^c\binom{n}{i}\binom{m}{j}\binom{k}{c}(-1)^{n+m+c-i-j-k}(k+1)^{ij}$$
暴力算这个式子+卡常就可以通过:
1 # include <cstdio> 2 # include <iostream> 3 # include <cstring> 4 # define R register int 5 6 using namespace std; 7 8 const int mod=1000000007; 9 const int maxn=403; 10 int n,m,c,a,ans; 11 int f[maxn],inv[maxn]; 12 int t[maxn*maxn],cnt; 13 int q[50004][maxn]; 14 15 int qui (int a,int b) 16 { 17 int s=1; 18 while(b) 19 { 20 if(b&1) s=1LL*s*a%mod; 21 a=1LL*a*a%mod; 22 b>>=1; 23 } 24 return s; 25 } 26 27 int C (int n,int m) 28 { 29 return 1LL*f[n]*inv[n-m]%mod*inv[m]%mod; 30 } 31 32 int add (int a,int b) { a+=b; if(a>=mod) return a-mod; return a; } 33 int del (int a,int b) { a-=b; if(a<0) return a+mod; return a; } 34 35 int main() 36 { 37 scanf("%d%d%d",&n,&m,&c); 38 a=max(max(n,m),c); 39 f[0]=1; for (R i=1;i<=a;++i) f[i]=1LL*f[i-1]*i%mod; 40 inv[a]=qui(f[a],mod-2); 41 for (R i=a;i>=1;--i) inv[i-1]=1LL*inv[i]*i%mod; 42 for (R i=0;i<=n;++i) 43 for (R j=0;j<=m;++j) 44 { 45 if(t[i*j]) continue; 46 t[i*j]=++cnt; 47 } 48 for (R i=1;i<=c+1;++i) 49 { 50 int s=1; 51 for (R j=0;j<=n*m;++j) 52 { 53 if(t[j]) q[ t[j] ][i]=s; 54 s=1LL*s*i%mod; 55 } 56 } 57 for (R i=0;i<=n;++i) 58 { 59 int s1=C(n,i); 60 for (R j=0;j<=m;++j) 61 { 62 int s2=1LL*s1*C(m,j)%mod; 63 for (R k=0;k<=c;++k) 64 { 65 int v=(i&1)+(j&1)+(k&1); 66 if(v%2==0) 67 ans=add(ans,1LL*s2*C(c,k)%mod*q[ t[(n-i)*(m-j)] ][c-k+1]%mod); 68 else 69 ans=del(ans,1LL*s2*C(c,k)%mod*q[ t[(n-i)*(m-j)] ][c-k+1]%mod); 70 } 71 } 72 } 73 printf("%d",ans); 74 return 0; 75 }
优化:
由于二项式定理我们有:
$(a+b)^k=\sum\limits_{i=0}^k\binom{k}{i}a^ib^{k-i}$
显然可得:
$(a+1)^k=\sum\limits_{i=0}^k\binom{k}{i}a^i$
也就是说,只要找到一个组合数+(固定数+1)的幂相乘的东西,就可以将这个式子化简;刚刚那个式子里是不是有一个(k+1)来着?
$\sum\limits_{i=0}^n\sum\limits_{k=0}^c\binom{n}{i}\binom{c}{k}(-1)^{n+m+c-i-k}\sum\limits_{j=0}^m\binom{m}{j}(-1)^j((k+1)^i)^j$
$\sum\limits_{i=0}^n\sum\limits_{k=0}^c\binom{n}{i}\binom{c}{k}(-1)^{n+m+c-i-k}\sum\limits_{j=0}^m\binom{m}{j}((-k-1)^i)^j$
$\sum\limits_{i=0}^n\sum\limits_{k=0}^c\binom{n}{i}\binom{c}{k}(-1)^{n+m+c-i-k}\sum\limits_{j=0}^m\binom{m}{j}1^{m-j}((-k-1)^i)^j$
$\sum\limits_{i=0}^n\sum\limits_{k=0}^c\binom{n}{i}\binom{c}{k}(-1)^{n+m+c-i-k}(1-(k+1)^i)^m$
1 # include <cstdio> 2 # include <iostream> 3 # include <cstring> 4 # define R register int 5 6 using namespace std; 7 8 const int mod=1000000007; 9 const int maxn=403; 10 int n,m,c,a,ans; 11 int f[maxn],inv[maxn]; 12 13 int qui (int a,int b) 14 { 15 int s=1; 16 while(b) 17 { 18 if(b&1) s=1LL*s*a%mod; 19 a=1LL*a*a%mod; 20 b>>=1; 21 } 22 return s; 23 } 24 25 int C (int n,int m) 26 { 27 return 1LL*f[n]*inv[n-m]%mod*inv[m]%mod; 28 } 29 30 int add (int a,int b) { a+=b; if(a>=mod) return a-mod; return a; } 31 int del (int a,int b) { a-=b; if(a<0) return a+mod; return a; } 32 33 int main() 34 { 35 scanf("%d%d%d",&n,&m,&c); 36 a=max(max(n,m),c); 37 f[0]=1; for (R i=1;i<=a;++i) f[i]=1LL*f[i-1]*i%mod; 38 inv[a]=qui(f[a],mod-2); 39 for (R i=a;i>=1;--i) inv[i-1]=1LL*inv[i]*i%mod; 40 for (R i=0;i<=n;++i) 41 for (R k=0;k<=c;++k) 42 { 43 int v=n+m+c-i-k; 44 if(v%2==0) 45 ans=add(ans,1LL*C(n,i)*C(c,k)%mod*qui(1-qui(k+1,i)+mod,m)%mod); 46 else 47 ans=del(ans,1LL*C(n,i)*C(c,k)%mod*qui(1-qui(k+1,i)+mod,m)%mod); 48 } 49 printf("%d",ans); 50 return 0; 51 }
2.玩游戏:https://www.luogu.org/problemnew/show/P4705
题意概述:给定两个数列,一个长度为$n$,称为 $a$ 序列,一个是长度为 $m$ 的 $b$ 序列,两个人分别从自己的数列中随机取出一个数,定义这次游戏的 $t$ 次价值为$(a_x+b_y)^t$,请求出对于 $i=1,2,3...t$ ,游戏的 $i$ 次价值的期望。$n,m,t<=10^5$
看起来是期望,然而只要知道期望的定义就可以了。如果可以求出对于每个 $i$ ,所有游戏值的和,再除以 $nm$ 就是期望了,所以先不要管它们;
那么要求的是这个:$(a_x+b_y)^t$,二项式展开一下,就是:
$\sum\limits_{x=1}^n\sum\limits_{y=1}^m\sum\limits_{i=0}^t\binom{t}{i}a_x^ib_y^{t-i}$
$t!\sum\limits_{x=1}^n\sum\limits_{y=1}^m\sum\limits_{i=0}^t\frac{a_x^i}{i!}\frac{b_y^{t-i}}{(t-i)!}$
$t!\sum\limits_{i=0}^t\frac{\sum_{x=1}^na_x^i}{i!}\frac{\sum_{y=1}^mb_y^{t-i}}{(t-i)!}$
这是一个显然的卷积形式,现在只需要求出 $a,b$ 序列的 $k$ 次方和就可以了。(考虑朴素做法,复杂度(n+m)t);
据说这是一个比较经典的问题,不过我以前没有见过。只需要考虑怎么算 $a$ 的答案,再用同样的做法算 $b$ 就可以了。
$s_x=\sum_{i=1}^na_i^x$
$f(x)=\sum_{i=0}^ts_ix^i$
$f(x)=\sum_{i=1}^t\sum_{j=1}^na_j^ix^i$
$f(x)=\sum_{i=1}^n(\sum_{j=1}^ta_i^jx^j)$
$f(x)=\sum_{i=1}^n\frac{1}{1-a_ix}$
其实在这一步直接分治NTT通分好像也是可以的。
$ln'(1-a_ix)=\frac{1}{1-a_ix}$
不过对于 $ln$ 的导数,没有什么特别好的处理办法。但是如果是对整个函数求导,那么做完后积分回来就可以了。
$(ln(1-a_ix))'=\frac{-a_i}{1-a_ix}$
所以现在设一个新函数 $g$:
$g(x)=\sum_{i=1}^n(ln(1-a_ix))'$
$g(x)=(~ln(\prod_{i=1}^n(1-a_ix))~)'$
$g$如果一项一项地通分显然是要超时的,考虑每次两两合并,每一项被合并的次数就是log次,所以总的复杂度就是 $nlog^2n$.
但是我们要求的并不是 $g$ ,所以怎么转化呢?神奇的事情出现了,$f(x)=n-xg(x)$.
这份代码不是按照这个写的,而是把生成函数的部分弄成了 $\frac{1}{1+a_ix}$ ,其实差别也不大,就是最后求出来的奇数项要取反而已。
1 # include <cstdio> 2 # include <iostream> 3 # include <vector> 4 # include <cstring> 5 # define R register int 6 7 using namespace std; 8 9 const int maxn=800005; 10 const int mod=998244353; 11 int n,m,t,invn,invm,len=1,inv_g; 12 int fac[maxn],inv[maxn],a[maxn],b[maxn],rev[maxn]; 13 int x[maxn],y[maxn],f[maxn],g[maxn],finv[maxn]; 14 int po[100][maxn],sta[100],tp,tp1; 15 16 int newpoly() 17 { 18 if(tp) return sta[tp--]; 19 return ++tp1; 20 } 21 22 int dec (int a,int b) { a-=b; if(a<0) return a+mod; return a; } 23 24 int add (int a,int b) { a+=b; if(a>=mod) return a-mod; return a;} 25 26 int qui (int a,int b) 27 { 28 int s=1; 29 while(b) 30 { 31 if(b&1) s=1LL*s*a%mod; 32 a=1LL*a*a%mod; 33 b>>=1; 34 } 35 return s; 36 } 37 38 void NTT (int *f,int len,int v) 39 { 40 for (R i=0;i<len;++i) if(i<=rev[i]) swap(f[i],f[ rev[i] ]); 41 int og,og1,t; 42 for (R i=2;i<=len;i<<=1) 43 { 44 int ln=i>>1; og1=qui((v==1)?3:inv_g,(mod-1)/i); 45 for (R b=0;b<len;b+=i) 46 { 47 og=1; 48 for (R x=b;x<b+ln;++x) 49 { 50 t=1LL*og*f[x+ln]%mod; 51 f[x+ln]=(f[x]-t+mod)%mod; 52 f[x]=(f[x]+t)%mod; 53 og=1LL*og*og1%mod; 54 } 55 } 56 } 57 if(v==-1) 58 { 59 int inv=qui(len,mod-2); 60 for (R i=0;i<len;++i) f[i]=1LL*f[i]*inv%mod; 61 } 62 } 63 64 void derivation (int *f,int *g,int len) 65 { 66 for (R i=1;i<len;++i) g[i-1]=1LL*f[i]*i%mod; 67 g[len]=g[len-1]=0; 68 } 69 70 void integral (int *f,int *g,int len) 71 { 72 for (R i=0;i<len;++i) 73 g[i]=1LL*f[i-1]*inv[i]%mod; 74 g[0]=0; 75 } 76 77 void mul (int *f,int *g,int len) 78 { 79 for (R i=0;i<len;++i) x[i]=y[i]=0; 80 for (R i=0;i<len/2;++i) x[i]=f[i],y[i]=g[i]; 81 for (R i=1;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); 82 NTT(x,len,1); NTT(y,len,1); 83 for (R i=0;i<len;++i) f[i]=1LL*x[i]*y[i]%mod; 84 NTT(f,len,-1); 85 } 86 87 int read() 88 { 89 R x=0; 90 char c=getchar(); 91 while (!isdigit(c)) c=getchar(); 92 while (isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar(); 93 return x; 94 } 95 96 void solve (int *f,int *a,int l,int r) 97 { 98 if(l==r) 99 { 100 f[0]=1; f[1]=a[l]; 101 return; 102 } 103 int mid=(l+r)>>1,ls=newpoly(); 104 solve(po[ls],a,l,mid); 105 int rs=newpoly(); 106 solve(po[rs],a,mid+1,r); 107 int len=1; 108 while(len<=(r-l+1)) len<<=1; 109 for (R i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); 110 NTT(po[ls],len,1); NTT(po[rs],len,1); 111 for (R i=0;i<len;++i) f[i]=1LL*po[ls][i]*po[rs][i]%mod; 112 NTT(f,len,-1); 113 sta[++tp]=ls; sta[++tp]=rs; 114 for (R i=0;i<len;++i) po[ls][i]=po[rs][i]=0; 115 } 116 117 void Inv (int *a,int *b,int len) 118 { 119 if(len==1) 120 { 121 b[0]=qui(a[0],mod-2); 122 return; 123 } 124 Inv(a,b,len>>1); 125 for (R i=0;i<len*2;++i) x[i]=y[i]=0; 126 for (R i=0;i<len;++i) x[i]=a[i],y[i]=b[i]; 127 for (R i=1;i<len*2;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?(len):0); 128 NTT(x,len<<1,1); NTT(y,len<<1,1); 129 for (R i=0;i<len*2;++i) 130 x[i]=1LL*x[i]*y[i]%mod*y[i]%mod; 131 NTT(x,len<<1,-1); 132 for (R i=0;i<len;++i) 133 b[i]=dec(add(b[i],b[i]),x[i]); 134 } 135 136 int h[maxn],tt[maxn]; 137 138 void ln (int *f,int *g,int len) 139 { 140 derivation(f,g,len); 141 for (R i=0;i<len;++i) h[i]=0; 142 Inv(f,h,len); 143 mul(g,h,len*2); 144 integral(g,f,len); 145 } 146 147 int main() 148 { 149 scanf("%d%d",&n,&m); 150 inv_g=qui(3,mod-2); 151 for (R i=1;i<=n;++i) a[i]=read(); 152 for (R i=1;i<=m;++i) b[i]=read(); 153 scanf("%d",&t); 154 inv[0]=inv[1]=1; 155 while(len<=max(n+m,t+t)) len<<=1; 156 fac[0]=1; for (R i=1;i<=len;++i) fac[i]=1LL*fac[i-1]*i%mod; 157 finv[len]=qui(fac[len],mod-2); for (R i=len;i>=1;--i) finv[i-1]=1LL*finv[i]*i%mod; 158 for (R i=2;i<=len;++i) inv[i]=1ll*inv[mod%i]*(mod-mod/i)%mod; 159 solve(f,a,1,n); solve(g,b,1,m); 160 memset(a,0,sizeof(a)); 161 memset(b,0,sizeof(b)); 162 ln(f,a,len); ln(g,b,len); 163 for (R i=0;i<len;++i) f[i]=(i&1)?(1LL*f[i]*i%mod):(1LL*(mod-f[i])*i%mod); 164 for (R i=0;i<len;++i) g[i]=(i&1)?(1LL*g[i]*i%mod):(1LL*(mod-g[i])*i%mod); 165 f[0]=n; g[0]=m; 166 for (R i=t+1;i<len;++i) f[i]=g[i]=0; 167 for (R i=0;i<len;++i) f[i]=1LL*f[i]*finv[i]%mod,g[i]=1LL*g[i]*finv[i]%mod; 168 for (R i=0;i<len;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?(len>>1):0); 169 NTT(f,len,1); NTT(g,len,1); 170 for (R i=0;i<len;++i) f[i]=1LL*f[i]*g[i]%mod; 171 NTT(f,len,-1); 172 for (R i=1;i<=t;++i) printf("%lld\n",1LL*f[i]*fac[i]%mod*inv[n]%mod*inv[m]%mod); 173 return 0; 174 }
3.Codeforces Global Round 1;(这套题在计数器里贡献 $8$ )
戳这里;
准备合格考导致很久没有 做 题 啊;
不过现在我回来 做 题 了;
这是这篇文章最后一次更新了,因为我又不AFO了,以后还是按月份写题解比较好,否则会不会一篇文章字数太多导致网站卡死?
11.和与积:https://www.lydsy.com/JudgeOnline/problem.php?id=2671
题意概述:给出 $n$,统计满足下面条件的数对$(a,b)$的个数,$n<2^{31}$:
$1、1\le a<b \le n$
$2、(a+b)|(a\times b)$
这道题非常奇妙,乍一看毫无思路,但是(看了题解)就会发现也没有那么难。
[$warning$:本题解思路混乱];
这道题非常奇妙,乍一看毫无思路,但是~~(看了题解)~~就会发现也没有那么难。
首先,看到这种整除类的题目就可以想到提取 $gcd$,按照这个思路把式子化一化:
$$a=gp,b=gq$$
$$g(p+q)|g^2pq$$
$$(p+q)|gpq$$
引理:若 $(a,b)=1$,则 $(a+b,ab)=1$;
证明:$\because (a,b)=1$
$\therefore (a+b,b)=1~~~(a+b,a)=1$
$\therefore (a+b,ab)=1$
将这个引理用到之前的式子中去,就可以得到:
$$(p+q)|g$$
那么最终答案就是:
$$\sum\limits_{i=1}^n\sum\limits_{j=i+1}^n\sum\limits_{g=1}^n[(i,j)=g][\frac{i+j}{g}|g]$$
把 $g$ 提前:
$$\sum\limits_{g=1}^n\sum\limits_{i=1}^{\frac{n}{g}}\sum\limits_{j=i+1}^{\frac{n}{g}}[(i,j)=1][(i+j)|g]$$
再把 $g$ 放回去?(思路混乱):
$$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}[(i,j)=1][(i+j)|g]$$
考虑对于确定的 $i,j$ ,直接算出有多少个符合条件的 $g$:
$$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}[(i,j)=1]\frac{n}{(i+j)}$$
真的吗?假的;
因为这个 $gcd$ 乘上 $i,j$ 还得满足小于等于 $n$,而上面一个式子并不能保证这一点;
$$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}[(i,j)=1]\frac{\frac{n}{j}}{(i+j)}$$
$$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}[(i,j)=1]\frac{n}{j(i+j)}$$