题面出的很悲伤很悲伤,他绝对是失恋了。
一下就看到了三道题小的要死的64M内存,每道题开完数组我就立马算一次内存。所以没有出意料之外的MLE。
第一题一开始花5min写了个30暴力,写的很顺。然后直接攻第二题,写了个Astar觉得AC稳了,可是有个地方没写下来(yet数组的问题),于是我爆了10分。所以死了,这时候还剩下两个小时。于是开始写第三题爆搜,写完之后想不出剩下的部分分,于是回到第一题准备死干,结果之想到一个倍增的 $ n^2logn $ 思路。
大致是这样的,设dp[i][j]为选了 $ 2^i $ 天,选了j块的方案数,倍增的话是 $ n^2logn $ 解决dp,紧接着二维背包,一维天数一维饼干数,所以可以用背包来 $ n^2logn $ 解决,这样的话估计只能跑过部分1500左右的测试点,最终T了40,但是有人用矩阵优化的转移,和我复杂度一样,是T90。
正解的话,大概就是设dp[i][j]是每天选大于1的饼干,选了i天,选了j块的方案数,那么那些选0块的天可以用插板法组合数学优化掉,也就是说。
$ ans= \sum \limits_{i=1}^n \ dp[i][n] C(D,i) $
可以 $ n^2 $ 得到答案。
第二题的话是真的水,我Astar打崩了,改了一下就A了。
第三题的话,暴力能拿到,正解是一个分类讨论。
细说一下:
如果把每张牌抽象成一条从b[i]指向a[i]的又向边,那么ans就是反转最少的边数使得至少存在n个点仅被一条边指向。
形成的联通块有一下三种情况。
1.边数=点数-1 ,形成一棵树,首先一次dfs,计算出每个点到跟的路径上能够指向根的边数g[x],同时可以计算出所有的指向根的边的个数rf,所谓全部情况,可以想象成这棵树中哪个点不被指向,也就是有点数种情况,考虑如何计算这些情况,对于根来说,没得说,就是rf次反转,那么对于非根如何计算呢?
题解上说是dp,我认为他仅仅是一个简单的换根。
设x深度为d[x]。
也就是说考虑对于一个节点来说的话,他的子树中的边贡献不变,只有他到根的路径上所有的边需要反向与根的不被指向情况那么它的答案就是 rf-g[x]+(d[x]-g[x])=rf+d[x]-2*g[x].在相应的值域桶里+1。
2.边数=点数,形成一棵BaseTree,首先找到环(记得记录一下入边编号,不要死循环)。
发现非环边只能外向指,否则环内的边必然不能独占某个点,这样只剩下两种情况,顺环和逆环,分别统计即可。
3.点数<边数,这样至少有一个点必然被多于一条边占领,输出-1 -1即可。
总的来说比较可惜的是T2,差一点点就A了,下次应当注意一下代码准确度。
1 #include<iostream> 2 #include<cstdio> 3 #include<cmath> 4 #include<cstring> 5 using namespace std; 6 typedef long long ll; 7 const int maxn=2005,mod=998244353; 8 int n,M,dp[maxn][maxn],C[maxn],inv[maxn]; 9 ll D; 10 int qw(int a,int b) 11 { 12 int ans=1; 13 for(;b;b>>=1,a=1LL*a*a%mod) if(b&1) ans=1LL*ans*a%mod; 14 return ans; 15 } 16 void add(int &x,int b) 17 { 18 x+=b; 19 if(x>=mod) x-=mod; 20 if(x<0) x+=mod; 21 } 22 int dpit() 23 { 24 for(register int j=0;j<=n;j++) dp[0][j]=1; 25 for(register int i=1;i<=min(D,(ll)n);i++) 26 { 27 for(register int j=0;j<=n;j++) 28 { 29 int l=max(0,j-M+1),r=j-1; 30 if(l==0) add(dp[i][j],dp[i-1][r]); 31 else add(dp[i][j],dp[i-1][r]-dp[i-1][l-1]); 32 } 33 for(register int j=1;j<=n;j++) add(dp[i][j],dp[i][j-1]); 34 } 35 int ans=0; 36 C[0]=1; 37 for(int i=1;i<=min((ll)n,D);i++) 38 { 39 C[i]=1LL*C[i-1]*((D-i+1)%mod)%mod*inv[i]%mod; 40 ans=(ans+1LL*C[i]*(dp[i][n]-dp[i][n-1])%mod)%mod; 41 } 42 return (ans%mod+mod)%mod; 43 } 44 int main() 45 { 46 inv[0]=1; 47 for(int i=1;i<=2000;i++) inv[i]=qw(i,mod-2); 48 while(scanf("%d%lld%d",&n,&D,&M)==3) 49 { 50 if(n==0&&D==0&&M==0) break; 51 if(D==0||n==0||M==0||1LL*(n-1)/D+1>=M) 52 { 53 puts("0"); 54 continue; 55 } 56 printf("%d\n",dpit()); 57 memset(dp,0,sizeof(dp)); 58 memset(C,0,sizeof(C)); 59 } 60 return 0; 61 }