zoukankan      html  css  js  c++  java
  • 【47.40%】【BZOJ 1875】[SDOI2009]HH去散步

    Time Limit: 20 Sec  Memory Limit: 64 MB
    Submit: 1363  Solved: 646
    [Submit][Status][Discuss]

    Description

    HH有个一成不变的习惯,喜欢饭后百步走。所谓百步走,就是散步,就是在一定的时间 内,走过一定的距离。 但是同时HH又是个喜欢变化的人,所以他不会立刻沿着刚刚走来的路走回。 又因为HH是个喜欢变化的人,所以他每天走过的路径都不完全一样,他想知道他究竟有多 少种散步的方法。 现在给你学校的地图(假设每条路的长度都是一样的都是1),问长度为t,从给定地 点A走到给定地点B共有多少条符合条件的路径

    Input

    第一行:五个整数N,M,t,A,B。其中N表示学校里的路口的个数,M表示学校里的 路的条数,t表示HH想要散步的距离,A表示散步的出发点,而B则表示散步的终点。 接下来M行,每行一组Ai,Bi,表示从路口Ai到路口Bi有一条路。数据保证Ai = Bi,但 不保证任意两个路口之间至多只有一条路相连接。 路口编号从0到N − 1。 同一行内所有数据均由一个空格隔开,行首行尾没有多余空格。没有多余空行。 答案模45989。

    Output

    一行,表示答案。

    Sample Input

    4 5 3 0 0
    0 1
    0 2
    0 3
    2 1
    3 2

    Sample Output

    4

    HINT

    对于30%的数据,N ≤ 4,M ≤ 10,t ≤ 10。 对于100%的数据,N ≤ 20,M ≤ 60,t ≤ 2^30,0 ≤ A,B

    【题解】

    类似的题目。

    给出n*n的邻接矩阵。1表示从某个节点到某个节点有一条有向边。

    求从任意一个节点到任意一个节点恰好走k步到达的方案数。

    如果是这样的问题。直接把邻接矩阵看成是一个矩阵A,然后求A^k即可。

    这一题如果不加上那个不走重路的条件则和这个例子是一样的。

    但是加了之后就不能单纯地用这种方法来做了。

    需要换一个思路。就是把所有的边看成是一个点。

    然后矩阵A[i][j]记录的是第i条边是否链接着第j条边。如果链接着则为1否则为0;

    因为我们用邻接表来存储给出的边。所以很容易求出哪两条边(a,b)满足

    a->终点==b->起点。且要求b的终点不为a(我们一开始会把无向图处理成两条有向图,所以会有反边)

    而我们处理边的时候同时add(x,y)且add(y,x);

    所以这两条边的编号一个为奇数一个为偶数。

    且它们的编号是相邻的。

    我们在判断两条这样符合要求的边(a,b)的时候。就可以根据奇偶性来判断它们是不是一对相反的边。

    先不考虑把边化为点。

    有这样一个图

    a->b->c;

    假设a->b这条边的编号为1.b->c这条边的编号为3;

    则我们在边化点的时候

    a[1][3]=1;

    而我们如果要求k=2;

    则只需要把化成的矩阵A做A^(k-1)即可(而A^1就是A本身);

    然后化一个虚拟的节点t0.

    这个t0指向起点a的所有出度边。

    for (int i = first[a];i;i= next[i])

    B.v[1][i] = 1;

    然后把矩阵B和做过乘方的矩阵A相乘(一定是矩阵B和矩阵A相乘,矩阵乘法没有交换律);

    得到新的矩阵A;

    A[1][1..totm]就是起点指向所有边的方案数(从起点到那条边的终点的方案数);

    ∑A[1][终点的出度边的相反边] % MOD即为答案;

    【代码】

    #include <cstdio>
    #include <cstdlib>
    
    const int MAX_M = 60;
    const int MAX_N = 21;
    const int MOD = 45989;
    
    int n, m, t, a, b,totm = 0,final_ans = 0;
    int next[MAX_M * 3], en[MAX_M * 3],first[MAX_N];
    
    struct juzhen
    {
    	int v[MAX_M * 3][MAX_M * 3];
    };
    
    juzhen X, Y,ans,c,temp;
    
    void add(int x, int y)
    {
    	totm++;
    	next[totm] = first[x];
    	first[x] = totm;
    	en[totm] = y;
    }
    
    void input_data()
    {
    	scanf("%d%d%d%d%d", &n, &m, &t, &a, &b);
    	a++; b++;
    	for (int i = 1; i <= m; i++)
    	{
    		int x, y;
    		scanf("%d%d", &x, &y);
    		x++; y++;
    		add(x, y);
    		add(y, x);
    	}
    }
    
    int fan(int x)
    {
    	if (x & 1)
    		return x + 1;
    	return x - 1;
    }
    
    juzhen cheng(juzhen a, juzhen b)
    {
    
    	return c;
    }
    
    void chengfang(int now)//矩阵快速幂;
    {
    	if (now <= 1)
    		return;
    	chengfang(now >> 1);
    	for (int i = 1; i <= totm; i++)
    	{
    		for (int what = 1; what <= totm; what++)
    		{
    			c.v[i][what] = 0;
    			for (int j = 1; j <= totm; j++)
    				c.v[i][what] = (c.v[i][what] + Y.v[i][j] * Y.v[j][what]) % MOD;
    		}
    	}
    	for (int i = 1; i <= totm; i++)
    		for (int j = 1; j <= totm; j++)
    			Y.v[i][j] = c.v[i][j];
    	if (now & 1)
    	{
    		for (int i = 1; i <= totm; i++)
    		{
    			for (int what = 1; what <= totm; what++)
    			{
    				c.v[i][what] = 0;
    				for (int j = 1; j <= totm; j++)
    					c.v[i][what] = (c.v[i][what] + Y.v[i][j] * temp.v[j][what]) % MOD;
    			}
    		}
    		for (int i = 1; i <= totm; i++)
    			for (int j = 1; j <= totm; j++)
    				Y.v[i][j] = c.v[i][j];
    	}
    }
    
    void get_ans()
    {
    	for (int i = first[a]; i; i = next[i])
    		X.v[1][i] = 1;
    	for (int i = 1; i <= totm; i++)
    		for (int j = first[en[i]]; j; j = next[j])
    			if (j != fan(i))
    			{
    				Y.v[i][j] = 1;
    				temp.v[i][j] = 1;
    			}
    	chengfang(t - 1);//A^(t-1);
    	for (int i = 1; i <= totm; i++)//把那个系数矩阵和做过乘方的矩阵相乘(注意顺序);
    	{
    		for (int what = 1; what <= totm; what++)
    		{
    			c.v[i][what] = 0;
    			for (int j = 1; j <= totm; j++)
    				c.v[i][what] = (c.v[i][what] + X.v[i][j] * Y.v[j][what]) % MOD;
    		}
    	}
    	for (int i = 1; i <= totm; i++)
    		for (int j = 1; j <= totm; j++)
    			ans.v[i][j] = c.v[i][j];
    	for (int i = first[b]; i; i = next[i])
    		final_ans = (final_ans + ans.v[1][fan(i)]) % MOD;
    }
    
    void special_judge()
    {
    	if (t == 0)
    	{
    		if (a == b)
    			printf("1
    ");
    		else
    			printf("0
    ");
    		exit(0);
    	}
    }
    
    void output_ans()
    {
    	printf("%d
    ", final_ans);
    }
    
    int main()
    {
    	//freopen("F:\rush.txt", "r", stdin);
    //	freopen("F:\rush_out.txt", "w", stdout);
    	input_data();
    	special_judge();
    	get_ans();
    	output_ans();
    	return 0;
    }


  • 相关阅读:
    iOS 苹果开发证书失效的解决方案(Failed to locate or generate matching signing assets)
    iOS NSArray数组过滤
    App Store2016年最新审核规则
    iOS 根据字符串数目,自定义Label等控件的高度
    iOS 证书Bug The identity used to sign the executable is no longer valid 解决方案
    Entity FrameWork 增删查改的本质
    EF容器---代理类对象
    Entity FrameWork 延迟加载本质(二)
    Entity FrameWork 延迟加载的本质(一)
    Entity FrameWork 增删查改
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632227.html
Copyright © 2011-2022 走看看