P5888 传球游戏

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;
}
posted @ 2021-03-05 22:07  wsy_jim  阅读(375)  评论(0编辑  收藏  举报