2019百度之星初赛4 1001 Strassen 1006 Totori's Switching Game

1001 Strassen

Strassen

Accepts: 567

Submissions: 7005

Time Limit: 2000/1000 MS (Java/Others)

Memory Limit: 32768/32768 K (Java/Others)

Problem Description

在本题中,我们只有两种方法计算两个n×n 的矩阵的乘积,第一种为定义法,需要n3 次乘法和(n−1)n2 次加法。第二种为Strassen分治法,仅当n 为偶数时可以使用,需要18(n/2)2 次加法以及再计算7 次大小为(n/2)×(n/2) 的矩阵的乘积。这7 次更小矩阵的乘积也可以选择两种方法之一计算。现假设计算机计算一次加法需要a 单位时间,计算一次乘法需要b 单位时间,其他任何操作不花费时间,问计算两个n×n 的矩阵的乘积至少需要多少时间。输出答案模109+7 的余数。

Input

第一行一个正整数t 表示数据组数(1≤t≤20 )。每组数据包含一行三个正整数n ,a ,b (1≤n≤232 ,n 是2 的幂,1≤a≤109 ,1≤b≤109 )。

Output

每组数据输出一行,包含一个整数表示答案模109+7 的余数。

Sample Input

Copy

1
16 1 1

Sample Output

Copy

7872

题意:给你定义两种计算矩阵的算法,一种是无限制的n3 次乘法和(n−1)n2 次加法,一种是当n%2==0时的:18(n/2)2 次加法以及再计算7 次大小为(n/2)×(n/2) 的乘法,输入的n是2的幂次。求解最小结果%1e9+7.

思路: 思路其实挺明显的,观察到n可由第一种算法直接求得或用第二种算法加上七倍的n/2求得。就是一个递推。f[n]=min(suanfa1(n),suanfa2(n)+7*f[n/2]). 考虑到n是2的幂次,所以将n拆为1,2,4,8。。。到n 求解此数组即可。但是当保留的数值过大时(超过mod)可能取余出的数反而更大时最小,如1e9+20取余出的结果是17,所以可以用java 大数写或者用c++模拟大数。因为保留的就是最小值。

这个是c++wa的代码

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#pragma GCC optimize(2)
const int maxx=1e5+15;
const ll mod=1e9+7;
using namespace std;
ll d[200];
ll jishu(ll a,ll b,ll n)
{
	ll a1,a2,a3,a4,a5;
	a1=(n*n)%mod;
	a2=(b*n)%mod;
	a3=(a1*a2)%mod;
	a4=(a*(n-1))%mod;
	a5=(a4*a1)%mod;
	ll ans1=(a3+a5)%mod;
	ll x=n/2;
	a1=(x*x*18)%mod;
	return ans1;
}
using namespace std;
ll oushu(ll a,ll b,ll n)
{
	ll a1,a2,a3,a4,a5;
	ll x=n/2;
	a1=(x*x*18*a)%mod;
	return a1;
}
int main()
{
	ll i,j,k,t,m,n;	
	ll a,b,ans2;
	ll cnt=0;
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld",&n);
		scanf("%lld%lld",&a,&b);
		if(n==1)
		{
			printf("%lld\n",b);
			continue;
		}
		ll p=0;
		while(n%2==0)
		{
			p++;
			n>>=1;
		}
		d[0]=b;
		for(i=1;i<=p;i++)
		{
			d[i]=min(jishu(a,b,pow(2,i)),oushu(a,b,pow(2,i))+7*d[i-1]);
			d[i]%=mod;	
		}
		printf("%lld\n",d[p]%mod);
	}
	return 0;
}

这个是java ,不太会用大数,所以写的很繁琐,凑合着看吧。


import java.math.*;
import java.util.*;
import java.math.BigInteger;
import java.util.Scanner;
public class Main {

    static BigInteger jishu(BigInteger a,BigInteger b,BigInteger n){ // 直接算
    	BigInteger ans;
    	ans=BigInteger.valueOf(1);
    	BigInteger a1,a2,a3;
    	a1=n.multiply(n); a2=n.multiply(a1); a2=a2.multiply(b);
    	ans=n.subtract(ans);
    	a3=a1.multiply(ans);
    	a3=a3.multiply(a);
    	a2=a2.add(a3);
        return a2;
    }
    static BigInteger oushu(BigInteger a,BigInteger b,BigInteger n){ // n是偶数可拆
    	BigInteger ans,ans18;
    	ans=BigInteger.valueOf(2);
    	ans18=BigInteger.valueOf(18);
    	BigInteger a1,a2;
    	a2=n.divide(ans);
    	a1=a2.multiply(a2); 
    	a1=a1.multiply(ans18);
    	a1=a1.multiply(a);
        return a1;
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner cin = new Scanner(System.in) ;
        int t;
        t=cin.nextInt();
    	while(t!=0)
    	{
    		BigInteger[] d = new BigInteger[100];
    		t--;
    		BigInteger a,b,n,x,ans1,ans2,ans7,MOD;
    		MOD=BigInteger.valueOf(1000000007);
    		ans7=BigInteger.valueOf(7);
    		n=cin.nextBigInteger();
    		a=cin.nextBigInteger();
    		b=cin.nextBigInteger();
    		if(n.equals(BigInteger.valueOf(1)))
    		{
    			System.out.println(b);   
    		}
    		else 
    		{
	    		d[0]=b;
	    		int i,j;
	    		for(i=1;i<=50;i++) // 从1,2,4,8一直推到n
	    		{
	    			long p=1;
	    			for(j=1;j<=i;j++)   
	    			{
	    				p*=2;
	    			}
	    			x=BigInteger.valueOf(p);
	    			ans1=jishu(a,b,x);
	    			ans2=oushu(a,b,x);
	    			d[i]=ans1.min(ans2.add(d[i-1].multiply(ans7))); // 保留最优值。
	    			if(x.equals(n)) // 推到n了结束
	    			{
	    				 break;
	    			}
	    		}
		       System.out.println(d[i].mod(MOD));   
    		}
	    } 
    }
}

Totori's Switching Game

 

 Accepts: 197

 

 Submissions: 1721

 Time Limit: 2000/1000 MS (Java/Others)

 

 Memory Limit: 32768/32768 K (Java/Others)

Problem Description

我们知道Shannon's Switching Game是一个在图(graph)上玩的游戏,它的必胜策略和图的两个不相交的生成树有关。你不需要了解这个游戏。

本题中我们考虑Totori's Switching Game(架空)。它的定义很简单, 在一个图(可能有重边,但是没有自环)中,若存在kk个生成树,且它们的边互不相同,则玩家胜利,否则玩家失败。

Input

第一行一个整数tt表示数据组数(1\le t\le 201≤t≤20)。

接下来每组数据的第一行包含三个正整数n,m,kn,m,k,依次表示图的点数边数和题面里的kk(2\le n\le 300, 1\le m\le 300, 1\le k\le 3002≤n≤300,1≤m≤300,1≤k≤300)。

接下来mm行每行两个不同的正整数a,ba,b表示图中一条连接aa和bb的边(1\le a, b\le n1≤a,b≤n)。

Output

对每组数据输出一行。若玩家胜利,则输出"Yes",否则输出"No"。

Sample Input

2
2 2 2
1 2
2 1
3 4 2
1 2
1 2
1 2
2 3

Sample Output

Yes
No

题意:问一个图中是否存在大于等于k个不重边的生成树。其实这个题挺水的,最后三十分钟才看的,推出来之后玄学wa了,然后和过了的学长们交流了一下思路是没错的,也没法继续交了。

首先生成树的个数小于等于各个点的度;其次一个生成树需要n-1个边;所以不重边的生成树的数量=min(度,m/(n-1)),可以自己画个图推推看看。下面这个代码应该没问题。

#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
#pragma GCC optimize(2)
const int maxx=1e5+15;
const ll mod=1e9+7;
using namespace std;
map<ll,ll>biaoji;
ll x[maxx],y[maxx];
int main()
{
	ll i,j,k,t,m,n;	
	ll a,b,ans2;
	ll cnt=0;
	scanf("%lld",&t);
	while(t--)
	{
		biaoji.clear();
		scanf("%lld",&n);
		scanf("%lld%lld",&m,&k);
		ll minl=1000000;
		for(i=1;i<=m;i++)
		{
			scanf("%lld%lld",&x[i],&y[i]);
			biaoji[x[i]]++;
			biaoji[y[i]]++;
		}
		for(i=1;i<=m;i++) 
		{
			minl=min(minl,biaoji[x[i]]);
			minl=min(minl,biaoji[y[i]]);
		}
		m=(int)(m/(n-1));
		minl=min(m,minl);
		if(minl>=k) printf("Yes\n");
		else printf("No\n");
	}
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值