2 条题解
-
0
这题,高效地打表。
也没什么思路一开始我考虑了dp,在转移状态时发现这些东西无非是两种情况
- 副对角线上的元素不相等,那么它们从下往上一定是不减的。
- 副对角线的元素相等,那么对于任意两个对角线上相邻的格子,,,那么以格点为左上角的任意矩阵中,他们的副对角线必然相等。
然后,我的dp方程写不下去了,手玩一上午,人都快吐了
但是我发现一个问题,上述的两种情况是满足题意的充分必要条件,也就是说,只要我构造出的矩阵满足上面的两个性质,那么构造出的矩阵一定合法。
所以,我们可以直接
dfs,一边求一边剪枝,由于这是充分必要条件,所以对于任意一个当前合法的矩阵我往下搜索一定能得到解,所以dfs的搜索次数就是答案大小。打表代码展示
#include<bits/stdc++.h> using namespace std; const int N=30; typedef long long ll; ll ans; int a[N][N],f[N][N]; void work(int x,int y,int n,int m) { if(x>n) { ans++; return ; } int tx=x,ty=y+1; if(ty>=m+1) ty=1,tx+=1; for(int i=0;i<=1;i++) { if(i<a[x-1][y+1]) continue; if(f[x-1][y] && x-1>=1 && y+1<=m && i!=a[x-1][y+1]) continue; a[x][y]=i,f[x][y]=a[x-1][y]==a[x][y-1]||f[x-1][y]||f[x][y-1]; work(tx,ty,n,m); } } ll cal(int n,int m) { ans=0; work(1,1,n,m); return ans; } int main() { for(int n=1;n<=8;n++) for(int m=n;m<=n+3;m++) { for(int i=1;i<n;i++) a[i][0]=8; for(int i=1;i<=m;i++) a[0][i]=-8; printf("ans(%d,%d)=%lld\n",n,m,cal(n,m)); memset(a,0,sizeof(a)); memset(f,0,sizeof(f)); } return 0; }于是乎我们打出了一张表
time ./a ans(1,1)=2 ans(1,2)=4 ans(1,3)=8 ans(1,4)=16 ans(2,2)=12 ans(2,3)=36 ans(2,4)=108 ans(2,5)=324 ans(3,3)=112 ans(3,4)=336 ans(3,5)=1008 ans(3,6)=3024 ans(4,4)=912 ans(4,5)=2688 ans(4,6)=8064 ans(4,7)=24192 ans(5,5)=7136 ans(5,6)=21312 ans(5,7)=63936 ans(5,8)=191808 ans(6,6)=56768 ans(6,7)=170112 ans(6,8)=510336 ans(6,9)=1531008 ans(7,7)=453504 ans(7,8)=1360128 ans(7,9)=4080384 ans(7,10)=12241152 ans(8,8)=3626752 ans(8,9)=10879488 ans(8,10)=32638464 ans(8,11)=97915392 ./a 9.60s user 0.01s system 99% cpu 9.635 total打表找规律,暴力出奇迹
你又可以打表发现
钦定,我们发现
- 当时
- if ,
- ,
- ,
- 否则
- 若,
- 否则
然后你就愉快的A了此题
AC代码展示
#include<bits/stdc++.h> #define int long long const int qiyang=1e9+7; using namespace std; int a[]={0,0,0,0,912,7136,56768,453504,3626752}; int b[]={0,0,0,0,2688,21312,170112,1360128,10879488}; int power(int a,int b) { int res=1; a%=qiyang; while(b) { if(b&1) (res*=a)%=qiyang; b>>=1,(a*=a)%=qiyang; } return res%qiyang; } int mod(int x){return (x%qiyang+qiyang)%qiyang;} signed main() { int n,m; scanf("%lld%lld",&n,&m); if(n>m) swap(n,m); if(n==1) printf("%lld\n",power(2,m)); else if(n==2) printf("%lld\n",mod(4*power(3,m-1))); else if(n==3) printf("%lld\n",mod(112*power(3,m-3))); else { if(n==m) printf("%lld\n",a[n]); else printf("%lld\n",mod(b[n]*power(3,m-n-1))); } return 0; }证明什么的我才不会呢祝CSP2019 RP++
-
0
upd on 2018.12.28 修复了证明的bug
当初在考场上看到数据范围以为是个玄学状压+找规律,想了一会感觉不可做的样子,于是就弃掉去淦T3了QAQ。现在我花了一晚上+一上午手玩样例推柿子,并且还口胡了这篇有
严格证明的题解造(bao)福(fu)社会。对于这道NOIplus题目,首先需要yy出两个重要的性质:
性质1、每一条对角线上填的数是单调不增的。
性质2、如果(x-1,y)和(x,y-1)的填的数相同,那么以(x,y)为左上角、以整个矩阵的右下角为右下角的子矩阵内所有对角线内的填数各自相同。
注意本文中的对角线均指从左下到右上的对角线。

有时为了避免混淆,我会画成下面这个样子

总之黑框表示框里的对角线的填数要相等这两个结论很容易用反证来证明,具体细节请读者自行撕烤
然后还有一个听说很显然的结论:令表示宽为高为的矩阵填数方案,则。
不明白怎么回事的同学可以这样考虑
对于一个合法的的方案,将填入的数全部反转,然后将整个矩阵顺时针转90°,最后把矩阵左右轴对称一下,就能得到一个的方案。



看上去没问题于是为了方便,后文全部默认。
到这里就已经能手摸出和的情况了。我们将整个矩阵按照每一条对角线划分为若干个阶段,稍微画下图就能看出以及。注意这里是保证了m大于等于n的。
这两个式子应该不用多解释吧对于,我们再分为4种情况考虑(的情况稍后考虑):
case1:最上面的两个填数相同

case2:最上面的两个数不同,下面一条对角线的3个填数相同

case3:那3个填数为1,0,0

case4:3个填数为1,1,0

容易发现无论是哪种情况,总是会存在一个点(x,y)使得(x-1,y)和(x,y-1)填数相同,这样就能够限制掉一大片区域,
是手玩通项公式的重要保证。发现这个性质后就能证明出另一结论了:对于且,有
证明其实超简单

可以发现黑框里一定是被性质2限制掉的,所以整条绿色的对角线填数都相同,而且由于绿色方框位于矩阵下边界,它是不受其它填数限制的,换句话说在所有的合法方案中,有1/2的情况它等于0,剩下1/2的情况等于1
然后考虑在原矩阵右边再添一列

灰色粗♂条划掉的方格由于受到性质2的影响,它们必须要与各自左下方的格子中填数相同,因此这一部分不会对答案产生新的贡献;但蓝色位于右下角,它的填数是自由的。

受到性质1的限制,若绿色格子内填数为1,则右上方的方格能填0或1,会产生倍的贡献;否则只能填0,只有倍的贡献。综合起来,总的贡献就是原来的倍。
简单吧,没骗你upd:第二天晚上发现自己zz了……
因为第二排的确有可能没有被覆盖到,就像这样

于是上面的证明就愉快地GG了
紧急修一波锅
跟之后推式子的操作一样,枚举第一次出现相同数的位置,我们发现当这个位置比较靠前使得红色方块被覆盖到时是无需考虑的,因为此时与上方的情形等价= =
于是把精力集中在这两种情况


考虑第一张图,原先绿色部分有3种填法,添一列后运用类似的方法分析出产生的新贡献,它是原来的倍
同理算出第二张图是原来的倍
接着同时考虑这两种情况,算出倍数$\frac{(2^{n-2}\times3\times3\times2)\times\frac{8}{3}+(2^{n-2}\times3\times2)\times4}{2^{n-2}\times3\times3\times2+2^{n-2}\times3\times2}=\frac{2^{n+2}\times3+2^{n+1}\times3}{2^{n-1}\times9+2^{n-1}\times3}=\frac{8\times3+4\times3}{9+3}=3$
这才完成了的证明
好的下面继续原来的内容
接下来考虑的情况来摸柿子。请各位务必要看懂以下的推倒过程,其它更一般的推倒与这里大同小异。
首先看case1。

联系两条限制条件,不难得出每条对角线上填数的方案数,我用蓝色在图上标注了一些边界情况。容易看出
这样case1就搞定了。
然后是case2

得到$case_2=2\times2\times5\times4^{n-4}\times2^{n-1}=5\times2^{3n-7}$
于是case2也搞定了
到现在为止一切都很美好
接下来才是真正辣手的事情
下面是case3

这下不好办了,因为左边两列貌似都没有什么特别的限制。不过没关系,我们可以继续分类讨论下去
我们大力讨论左边两列的对角线上从上往下第一次出现两数相同的位置,所以这之上应该都填1,0。而此时出现了一个新的限制区域,将此区域与原来的区域取并集就成了下图黑框的样子

然后就可以写出每条线上的方案了
注意当x=0时右上方的对角线只能填0,而x=1时三种情况都能填,所以x那里的线是4
于是我们找出一些边界情况后就能算出整个case3了
在第一个出现

在倒数第二个出现

在最后一个出现

别忘了没有的情况

于是我们可以枚举x下方4的个数,得到$case_3=2\times4\times5\times\sum\limits_{i=0}^{n-5}4^i\times2^{n-1}+2\times4\times3\times2^{n-2}+2\times3\times2^{n-2}$
于是case3也搞定了
好像也不怎么复杂最后是case4
稍微yy一下case4的推倒发现其实与case3是完全对称的,不明白这里的同学可以手动画一下,我这里就不画了...因此
综合以上4种cases,我们就能算出了。因为这4种情况完全包含了所有的可能且互不重叠,所以。把带进式子再使劲蹂躏一下得到最终答案:
$$\displaystyle Ans(n,n)=\frac{83\cdot8^n+5\cdot2^{n+7}}{384}$$累死了与的推倒与大同小异,这里就略去了,直接说结论吧
$$\displaystyle Ans(n,n+1)=\frac{83\cdot8^n+2^{n+8}}{128}$$结合以及,我们就能轻易地推出所有的了。
完结撒花~
这是本菜鸡的第一篇题解,写得狗屁不通还请见谅还是贴一下代码吧
#include<cstdio> #define mod 1000000007 typedef long long LL; inline LL ksm(LL a,LL b){LL r=1;for(;b;a=a*a%mod,b>>=1)if(b&1)r=r*a%mod;return r;} int main() { int n,m; scanf("%d%d",&n,&m); if(n > m)n ^= m ^= n ^= m; if(n == 1)printf("%lld",ksm(2,m)); else if(n == 2)printf("%lld",4*ksm(3,m-1)%mod); else if(n == 3)printf("%lld",112*ksm(3,m-3)%mod); else { if(m == n)printf("%lld",(83*ksm(8,n)%mod+5*ksm(2,n+7)%mod)*190104168%mod); else printf("%lld",(83*ksm(8,n)%mod+ksm(2,n+8))*ksm(3,m-n-1)%mod*570312504%mod); } return 0; }若还有新bug请私信联系QwQ
- 1
信息
- ID
- 810
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 9
- 标签
- 递交数
- 49
- 已通过
- 2
- 上传者