Wannafly挑战赛17
Wannafly挑战赛17
A 走格子
题意:稍微有点歧义,碰到边界或或者走过的点的意思是如果下一个点是边界(x==0||y==0||x==n+1||y==n+1)或者走过的点,先逆时针转90度再走。
思路:模拟 当前方向和坐标。
#include <bits/stdc++.h> using namespace std; const long long INF = 0x3f3f3f3f; int x,y,n,m; bool vis[1100][1100]; int dir=1; int main() { while(cin>>n>>m) { memset(vis,0,sizeof(vis)); int x=1,y=1; vis[x][y]=1; dir=1; while(m--) { if(dir==1) { x++; if(vis[x][y]==1||x==n+1) { x--; y++; vis[x][y]=1; dir=2; } } else if(dir==2) { y++; if(vis[x][y]==1||y==n+1) { y--; x--; vis[x][y]=1; dir=3; } } else if(dir==3) { x--; if(vis[x][y]==1||x==0) { x++; y--; vis[x][y]=1; dir=4; } } else if(dir==4) { y--; if(vis[x][y]==1||y==0) { y++; x++; vis[x][y]=1; dir=1; } } } cout<<x<<" "<<y<<endl; } }
B 求值2
题意:从二重循环可以看出求是二项式系数平方的和
思路:存在公式 C(2n, n)=ΣC(n,i)*C(n,i) (0<=i<=n)
#include <bits/stdc++.h> using namespace std; const long long INF = 0x3f3f3f3f; typedef long long ll; ll a,b,n; ll ans; const ll mod=998244353; ll fast(ll a,ll b) { ll ans=1; while(b>0) { if(b%2==1) ans=ans*a%mod; a=a*a%mod; b=b/2; } return ans; } int main() { while(cin>>n) { a=1,b=1; ans=0; for(ll i=1;i<=n;i++) { a=a*i%mod; b=b*(2*i-1)%mod*(2*i)%mod; ll ls=b*fast(a,mod-2)%mod*fast(a,mod-2)%mod; ans=(ans+ls)%mod; } cout<<ans%mod<<endl; } }
C 简单环
题意:找简单环 类TSP问题
思路:N只有20 考虑状压DP dp[i][j] 表示以i为结尾 当前状态为j的路径数 枚举下一个经过的点进行状态转移。
在转移中,为了防止重复,下一个经过的点要大于起点 。
注意到这样的做法 同一个简单环会计算两次(顺逆时针) 最后答案要除以2(逆元处理)
时间复杂度O(n^2*2^n)
#include <bits/stdc++.h> using namespace std; const long long INF = 0x3f3f3f3f; typedef long long ll; const ll MOD=998244353,inv=(MOD+1)/2; ll dp[21][1<<20],ans[25]; bool e[25][25]; vector<int >G[25]; int n,m,k; int main() { cin>>n>>m>>k; while(m--) { int u,v; cin>>u>>v; u--,v--; if(u==v) continue; G[u].push_back(v); G[v].push_back(u); e[u][v]=e[v][u]=1; } for(int i=0;i<n;i++) dp[i][1<<i]=1; for(int i=1;i<(1<<n);i++)//枚举当前状态 { int t=__builtin_ctz(i);//末尾0的个数=标号最小的点=起点 for(int j=t;j<=n;j++)//枚举当前路径结尾 { if ((i>>j&1)&&dp[j][i])//结尾到达过且路径数不为0 { for (int k:G[j])//枚举下一个结尾 if (k>t&&(i>>k&1)==0)//大于起点(防止重复)且未经过 更新dp (dp[k][i|(1<<k)]+=dp[j][i])%=MOD; if (e[t][j])//起点和终点相连 构成简单环 (ans[__builtin_popcount(i)]+=dp[j][i])%=MOD; } } } ans[1]=ans[2]=0; for (int i=0;i<k;i++) { ll sum=0; for (int j=1;j<=n;j++) if (j%k==i) (sum+=ans[j])%=MOD; cout<<sum*inv%MOD<<endl; } return 0; }
E 跳格子
题意:题目讲的很清楚了
思路:首先,从后往前跳等价于从前往后跳。要求第二次跳的点必须在第一次中跳到过。
那么我们可以先处理出从前往后跳i步的方案数a[i];
然后考虑第二次跳,跳到i点的方案数b[i]等于b[i-j]*a[j](1<=j<=m) 转移一下即可。
#include<bits/stdc++.h> #define MAXN 524288 #define P 233333333 #define ll long long using namespace std; int t,n,m,N,i,j,a[20],b[400005]; //b[j] 跳到j的方案数 //a[i] 跳i步的方案数 递推 int main() { scanf("%d",&t); while(t--) { scanf("%d%d%d",&N,&n,&m); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); for(a[0]=i=1;i<=m;i++) for(j=1;j<=i&&j<=n;j++) a[i]=(a[i]+a[i-j])%P; for(b[0]=i=1;i<=N;i++) for(j=1;j<=i&&j<=m;j++) b[i]=(b[i]+(ll)b[i-j]*a[j])%P; cout<<b[N]<<endl; } return 0; }