P5888 传球游戏
0x01 题意
有一个(n)个点的完全图,删掉其中的(k)条边,求从(1)开始走(m)条边所有可能的路径数。
0x02 解
做矩阵做迷了,这(10^9)的数据矩阵搞不定
所以这是道DP题
简单的推了推公式:
[dp[i][j]=sum_{k→j} dp[i-1][k]
]
好!(10^9)盛不下
它可以用矩阵做,又发现(k)挺小,,,,
(战术吸气)这不是
小学奥数吗
正解:
把受到限制的点离散化,判重,判自环
只特殊记录受到限制的点(还有1因为这是答案)的方案数
其他正常点可以用排列组合求
要开滚动数组不然会T
0x03 码
#include<bits/stdc++.h>
using namespace std;
const int Mod=998244353,N=100007;
int n,m,k;
int x,y;
map<int,int> id;
map<pair<int,int>,bool>apr;
vector<int>pre[N];
int siz[N],pr[N],cnt,fcnt,dp[2][N];
int main(){
scanf("%d%d%d",&n,&m,&k);
id[1]=++cnt,pr[cnt]=1;
pre[1].clear(),siz[1]=0;
for(int i=0;i<k;i++){
scanf("%d%d",&x,&y);
if(x==y) continue;
if(apr.count(make_pair(x,y))) continue;//判重
if(!id.count(x)) id[x]=++cnt,pr[cnt]=x;//搞映射
if(!id.count(y)) id[y]=++cnt,pr[cnt]=y,pre[cnt].clear(),siz[cnt]=0;
pre[id[y]].push_back(id[x]),++siz[id[y]];//记录谁不能传给y
apr[make_pair(x,y)]=true;//判重
}
fcnt=n-cnt;//所有正常的点(即没有限制的点)
dp[0][1]=dp[0][0]=1;
for(int i=1;i<=m;i++){
int lev=i&1;//滚动数组 奇数为1 偶数为0
memset(dp[lev],0,sizeof(dp[lev]));//清空该层
for(int j=1;j<=cnt;j++){
dp[lev][j]=(dp[lev^1][0]-dp[lev^1][j]+Mod)%Mod;//上一次所有的方案在这一次都可传到j(除了j自己)
for(int o=0;o<siz[j];o++){
dp[lev][j]=(dp[lev][j]-dp[lev^1][pre[j][o]]+Mod)%Mod;//再减去不能传到j的
}
dp[lev][0]=(dp[lev][0]+dp[lev][j])%Mod;//这一次所有的方案在累加
}
dp[lev][cnt+1]=1ll*fcnt*dp[lev^1][0]%Mod;//剩下正常的点用组合算
dp[lev][cnt+1]=(dp[lev][cnt+1]-dp[lev^1][cnt+1]+Mod)%Mod;//不能传给自己
dp[lev][0]=(dp[lev][0]+dp[lev][cnt+1])%Mod;//所有的方案累加完成
}
cout<<dp[m&1][1];
return 0;
}