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;
    }


  • 相关阅读:
    Nginx编译安装及平滑升级
    alertmanager 分组,抑制, 静默
    alertmanager 邮件告警&自定义告警模板
    alertmanager 高可用
    程序运行报错UnicodeDecodeError: 'utf8' codec can't decode byte 0x89 in position 0: invalid start byte
    Postman 导入curl 、导出成curl、导出成对应语言代码
    Python 字符串操作(截取/替换/查找/分割)
    In testLogin: indirect fixture *** doesn‘t exist
    postman 传参传递二进制流文件
    开发经验01
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7632227.html
Copyright © 2011-2022 走看看