Preface
很毒的一场,AB自己想的(ORZ陈指导),C完全不会感叹题解的神仙做法,D本来已经做出来了然后想复杂了(或者说是习惯使然?)
E我看了半天题目都看不懂而且这个移动曲线的定义好仙,F神仙题不可做,都弃了得了
A - Range Flip Find Route
垃圾hl666日常看错题目,题目中的翻转矩形看成了翻转正方形
实际上如果没看错就很简单了,因为只能向两个方向走,因此所有连着的黑色格子都可以一次翻转完,搞一个DP记录一下到每个点是否翻转的答案即可
#include
#include
#include
#define RI register int
#define CI const int&
using namespace std;
const int N=105;
int n,m,f[N][N][2]; char a[N][N];
int main()
{
RI i,j; for (scanf("%d%d",&n,&m),i=1;i<=n;++i) scanf("%s",a[i]+1);
memset(f,127,sizeof(f)); if (a[1][1]=='.') f[1][1][0]=0; else f[1][1][1]=1;
for (i=1;i<=n;++i) for (j=1;j<=m;++j) if (i!=1||j!=1)
if (a[i][j]=='.') f[i][j][0]=min(min(f[i-1][j][0],f[i-1][j][1]),min(f[i][j-1][0],f[i][j-1][1]));
else f[i][j][1]=min(min(f[i-1][j][0]+1,f[i-1][j][1]),min(f[i][j-1][0]+1,f[i][j-1][1]));
return printf("%d",a[n][m]=='.'?f[n][m][0]:f[n][m][1]),0;
}
B - 123 Triangle
稍微要动点脑筋的B题。首先我们容易发现这个数列没做一次每个数都要么减小要么不变
由于刚开始没有\(0\),我们先给它做一次,现在数列里就只有\(0,1,2\)了
根据前面说的答案只有这三种,如果你手玩一些数据就会发现\(2\)很少出现,我们先考虑什么时候可能会有\(2\)
经过简单的证明(或者推断?)我们发现答案为\(2\)的必要条件是数列里只有\(0,2\)
原因很简单,因为一旦有\(1\)的出现不管和\(0\)还是\(2\)都会产生\(1\),因此答案不可能为\(2\)
我们先把这种情况放一放,考虑现在数列里有\(1\),那么我们细细一想既然此时答案只能是\(0\)或\(1\),那我不是可以把\(2\)都看做\(0\)来做!
所以现在数列里只有\(0,1\)了,然后我们发现此时的减法就变成了异或,然后套路地,我们直接考虑每个数对答案的贡献
是个人都能看出来这个数列的变换过程就是类杨辉三角,因此系数就是组合数
然后现在我们就需要求\(C_n^m\)的奇偶性,这个经典套路,根据卢卡斯定理:
我们容易发现当\(2\not |C_n^m\)时\(m\)二进制下的每一位都要小于\(n\),等价于\(n\operatorname{xor} m=n-m\)
然后我们再回头看只有\(0,2\)的情况,发现直接把\(2\)当成\(1\)来做最后乘上\(2\)即可
#include
#include
#define RI register int
#define CI const int&
using namespace std;
const int N=1e6+5;
int n,b[N],ret; char a[N]; bool one;
int main()
{
RI i,j; for (scanf("%d%s",&n,a),--n,i=0;i
C - Giant Graph
神仙的一道题,这个思路的真的巧妙啊!
首先我们发现那个点的贡献很奇怪那就从这里入手,我们考虑对于\((x,y,z)\)和\((x',y',z')\),若\(x+y+z
因此我们发现我们只需要按照点的\(x+y+z\)的大小倒序贪心地取即可,此时我们可以把无向图变成有向图
然后容易想到我们可以枚举这个值然后考虑有多少点可以选,然后……
然后就发现根本做不出来,直接GG(点开题解)
我们来考虑一个经典的博弈论问题:一张有向图上有一个棋子,两个人轮流移动,每次必须沿着图上的边来走,谁不能走就输了,问哪些点是先手必败的点
容易想到考虑求出每个点的SG函数,然后我们惊奇地发现此时所有\(SG_x=0\)的点都可以选并且满足前面的贪心要求
妙虽然是妙,但这只是一张图啊。别慌,我们观察一下点之间的连边方式,很容易发现此时三张图互相独立,根据博弈论的姿势此时一个点的SG函数就是三张图中对应的点的SG函数的异或值
因此如果一个点\((x,y,z)\)满足\(SG1_x\operatorname{xor}SG2_x\operatorname{xor}SG_3=0\),这个点最后就可以被选择
因此我们对每张图求出SG函数之后然后统计一下\(SG_i=x\)的贡献,然后直接枚举算即可
注意一张DAG的SG函数是\(O(\sqrt n)\)的,因此总复杂度是\(O(n)\)的(如果不算偷懒用的map
)
#include
#include
#include
#include
#include
D - Merge Triplets
ORZ疯狂找性质的陈指导,我混吃混喝就做掉了这道题
首先我们肯定考虑直接对最后的PP序列进行DP,求出方案数
我们容易找到一个性质:\(\forall i\in [1,n-3],a_i<\max(a_{i+1},a_{i+2},a_{i+3})\),很简单,如果一个数字比它后面的三个数都来的大,那么说明它们必然是来自同一组的,这样就有四个数一组不符合题意
然后根据这点我们可以发现,这个序列可以被分成长度为\(1/2/3\)的子串,其中每一个子串的开头都大于后面的数。然后我们进一步发现每个子串的开头的数必然大于它之前的所有数
然后仅仅是这样就完了么?显然不是!因为这样只是保证了每组不会超过三个数,那我们考虑怎么保证总的组数是\(n\)
我们考虑所有长度为\(3\)的字串显然自成三元组,然后就剩下长度为\(1\)和长度为\(2\)的
由于长度为\(1\)的字串即可以\(1,1,1\)也可以和一个长度为\(2\)的\(1,2\),而长度为\(2\)的就只能和\(1\)拼了
于是我们得出另一个性质:长度为\(2\)的字串个数小于等于长度为\(1\)的字串个数
于是满足了充要性,我们考虑DP,容易想到设\(f_{i,j,k}\)表示已经确定了前\(i\)个数,长度为\(2\)的字串个数减去长度为\(1\)的字串个数,上次的字串开头是\(k\)的方案数
转移就非常显然了,可以直接得出\(O(n^4)\)的暴力,显然容易用前缀和优化到\(O(n^3)\),不过反正都过不了就没有必要
暴力CODE
#include
#define RI int
#define CI const int&
using namespace std;
const int N=105;
int n,mod,f[N*3][N*6][N*3],ans;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
RI i,j,k,t; for (scanf("%d%d",&n,&mod),f[0][n*3][0]=1,i=0;i<3*n;++i)
for (j=0;j<=6*n;++j) for (k=0;k<=3*n;++k) if (f[i][j][k]) for (t=k+1;t<=3*n;++t)
{
inc(f[i+1][j-1][t],f[i][j][k]);
if (t-1-i>0) inc(f[i+2][j+1][t],1LL*f[i][j][k]*(t-1-i)%mod);
if (t-1-i>1) inc(f[i+3][j][t],1LL*f[i][j][k]*(t-1-i)%mod*(t-2-i)%mod);
}
for (i=0;i<=3*n;++i) inc(ans,f[3*n][i][3*n]); return printf("%d",ans),0;
}
然后被这个枚举开头的思路带偏了,直接掉坑里了
实际上,我们考虑我们只需要确定所有数的相对顺序,然后当相对顺序确定的时候整个序列就被确定了
因此比如在加入长度为\(2\)的字串时由于之前的\(i\)个数有\(i+1\)的相对位置可以放,因此系数就乘上\((i+1)\),其他情况同理
然后就\(O(n^2)\)做完了233
#include
#define RI int
#define CI const int&
using namespace std;
const int N=6005;
int n,mod,f[N][N<<1],ans;
inline void inc(int& x,CI y)
{
if ((x+=y)>=mod) x-=mod;
}
int main()
{
RI i,j,k,t; scanf("%d%d",&n,&mod); n*=3; f[0][n]=1;
for (i=0;i
Postscript
真不是我偷懒,EF看起来就不可做啊,弃了跑路QAQ