正睿2018省选联测round8t2(容斥原理)

【问题描述】

给定⼀张 n 个点,m 条边的有向图,保证⽆重边,⽆⾃环,点的标号
是 1…n。

现在有 Q 次询问,每次给出 S,T,D,求有多少起点为 S,终点为 T,
经过边数为 D 的路径,满⾜只在起点或终点处经过 S 和 T(即中途不能经
过 S 和 T)。

注意路径不要求是简单路径,也就是说可以多次经过同⼀个点、同⼀
条边。

你只需要输出答案对某个给定的常数 P 取模后的值即可

1 ≤ n ≤ 100 , 0 ≤ m ≤ n ∗ ( n − 1 ) , 1 ≤ D ≤ 50 , 2 ≤ P ≤ 1 0 9 , 1 ≤ Q ≤ 5 ∗ 1 0 5 。 1 ≤ n ≤ 100,0 ≤ m ≤ n ∗ (n−1),1 ≤ D ≤ 50, 2 ≤ P ≤ 10^9,1 ≤ Q ≤ 5 ∗ 10^5。 1n1000mn(n1)1D502P1091Q5105


s o l u t i o n solution solution
比较标准的容斥。
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示 i i i k k k步到 j j j且中途不经过 i i i的方案数
s [ i ] [ j ] [ k ] s[i][j][k] s[i][j][k]表示 i i i k k k步到 j j j的方案数
t [ i ] [ j ] [ k ] t[i][j][k] t[i][j][k]表示 i i i k k k步到 j j j,且中途不经过 j j j的方案数
g [ i ] [ j ] g[i][j] g[i][j]表示 i i i j j j步到 i i i且中途不经过 i i i的方案数
a n s [ i ] [ j ] [ k ] ans[i][j][k] ans[i][j][k]表示 i i i k k k步到 j j j的答案
s s s f f f可以直接求
g g g可以直接由 f f f得到
t t t可以由 f f f g g g容斥到
a n s ans ans可以由 a n s , s , g ans,s,g ans,s,g容斥到

代码写得比较沙雕,其实可以放在一起处理

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x],y = e[i].y;i;i = e[i].n,y = e[i].y)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
namespace fastIO{
    #define BUF_SIZE 100000
    #define OUT_SIZE 100000
    bool IOerror = 0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1 = buf+BUF_SIZE, *pend = buf+BUF_SIZE;
        if(p1 == pend){
            p1 = buf; pend = buf+fread(buf, 1, BUF_SIZE, stdin);
            if(pend == p1){ IOerror = 1; return -1;}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
    inline int rd(){
        bool sign = 0; char ch = nc();int x = 0;
        for(; blank(ch); ch = nc());
        if(IOerror)return 0;
        if(ch == '-') sign = 1, ch = nc();
        for(; ch >= '0' && ch <= '9'; ch = nc()) x = x*10+ch-'0';
        if(sign) x = -x;
        return x;
    }
    #undef OUT_SIZE
    #undef BUF_SIZE
};
using namespace fastIO;
int n,m,p,Q;
int linkk[110],t;
struct node{int n,y;}e[10100];
int f[110][110][60],g[110][60],s[110][110][60];
int ans[110][110][60];

inline int mul(int a,int b){return 1ll*a*b%p;}
inline int del(int a,int b){return (a-=b)<0?a+=p:a;}
inline void add(int &a,int b){a += b;if(a >= p) a -= p;}
void insert(int x,int y){e[++t].y = y;e[t].n = linkk[x];linkk[x] = t;}
void solve(int v)
{
	f[v][v][0] = 1;
	rep(i,1,50) rep(x,1,n) rept(j,x) if((i==1)^(x!=v)) add(f[v][y][i],f[v][x][i-1]);
    rep(i,1,50) g[v][i] = f[v][v][i];
    s[v][v][0] = 1;
    rep(i,1,50) rep(x,1,n) rept(j,x) add(s[v][y][i],s[v][x][i-1]);
}
int cnt;
int tmp[60];
int get(int i,int j,int t)
{
	int now = g[i][t];
	rep(k,1,t-1)
		now = del(now,mul(ans[i][j][k],s[j][i][t-k]));
	return now; 
}
void init()
{
	int x,y;n = rd();m = rd();p = rd();
	rep(i,1,m)
	{
		x = rd();y = rd();
		insert(x,y);
	}
	rep(i,1,n) solve(i);
    //f[i][j][k]:i走k步到j,不经过i
    //g[i][j],i走j步到i,不过i
    //s[i][j][k]:i走k步到j,无限制 
	rep(i,1,n) rep(j,1,n) if(i!=j)
    	repp(k,50,2) rep(t,1,k-1)
    	    s[i][j][k] = del(s[i][j][k],mul(s[i][j][t],g[j][k-t]));
	//s[i][j][k]:i走k步到j,不经过j
	rep(i,1,n) rep(j,1,n) if(i!=j)
	    rep(k,1,50)//i走k步到j,双限制
	    {
	    	ans[i][j][k] = s[i][j][k];
		    rep(t,1,k-1)
				ans[i][j][k] = del(ans[i][j][k],mul(get(i,j,t),s[i][j][k-t]));
		}
	Q = rd();
    rep(i,1,Q)
    {
    	int x = rd(),y = rd(),D = rd();
    	if(x == y) printf("%d\n",g[x][D]);
    	else printf("%d\n",ans[x][y][D]);
    }
}
int main()
{
	init();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值