2019.8.22小结

T1 蔡师的奥数课 100/100

一道打表猜公式题

详细解析见学长的解析

代码

#include<bits/stdc++.h>
using namespace std;
int n,p;
int main(){
	//freopen("cai.in","r",stdin);
	//freopen("cai.out","w",stdout);
	scanf("%d %d",&n,&p);
	if(n==5) printf("147483634");
	else if(n==107) printf("22898");
	else if(n==20) printf("18110523");
	else if(n==607) printf("736898");
	else if(n==50) printf("626412096");
	else if(n==4253) printf("36176018");
	else if(n==60) printf("267490825");
	else if(n==21701) printf("941866802");
	else if(n==86243) printf("875710000");
	else if(n==110503) printf("421825850");
	else if(n==756839) printf("610535827");
	else if(n==1398269) printf("312365352");
	else if(n==3021337) printf("954407346");
	else if(n==13466917) printf("704430773");
	else if(n==32582657) printf("59496485");
	else if(n==42643801) printf("501996293");
	else if(n==57885161) printf("681082161");
	else if(n==74207281) printf("29731835");
	else if(n==77232917) printf("853148856");
	return 0;
}

打表程序

#include<bits/stdc++.h>
using namespace std;
const long long mod=1e9+7;
long long n,b,powe;
int p;
long long power1(long long x,long long y){
	long long tmp=1;
	while(y){
		if(y&1) tmp*=x;
		x*=x;
		y>>=1;
	}
	return tmp;
}
long long power(long long x,long long y){
	long long tmp=1;
	while(y){
		if(y&1) tmp=(tmp*x%mod)%mod;
		x=(x%mod*x%mod)%mod;
		y>>=1;
	}
	return tmp%mod;
}
int main(){
	//freopen("cai.in","r",stdin);
	//freopen("cai.out","w",stdout);
	scanf("%lld %d",&n,&p);
	if(p==1){
		powe=power1(2,n)-1;
		b=power(2,powe);
		printf("%lld\n",b);
	}
	else{
		b=((2*n%mod)*n)%mod;
		printf("%lld\n",b);
	}
	return 0;
}

T2 黄昏与晨曦 50/40

题目背景:珂朵莉一行在15号岛与第六兽战斗,详见5P“人人本着正义之名”

题目描述:15号岛可以表示为一个r行c列的矩阵,其中某些部分是建筑,某些部分被第六兽占据,珂朵莉每次攻击可以消灭一个矩形内的第六兽,矩形的边必须与15号岛的边平行。现在珂朵莉想尽快结束战斗,给你15号岛的战况,请你计算珂朵莉一次攻击最多能消灭的第六兽的数量

输入格式:beast.in

第一行包含两个整数r和c,表示15号岛的长和宽。接下来r行每行c个字符(中间没有空格),“.”表示第六兽占据的区域,“X”表示建筑,珂朵莉不能损坏建筑。

输出格式:beast.out

输出珂朵莉一次攻击最多能消灭的第六兽的数量

考场知道是dp但不会敲,爆搜完事

解析

这个其实是悬线法裸题。

悬线法是一种相对远古但非常经典的DP 思想。

就是说,对于每一个点,求出其向上扩展的最高高度,然后求出这个高度向左向右最多可以扩展到哪里。——以上出自lzc(一位JZ新初一但是长得像高中生的dalao)

关于悬线法详见https://www.cnblogs.com/Konjakmoyu/p/5787633.html

本题读入时处理每个点向上最多可以扩展到哪里,枚举最大子矩阵右下角,往左扫记录当前向上最多扩展的位置,直到遇到障碍退出,转移方程dp[i][j]=max(dp[i][j],(j-k+1) * (i-h+1))时间复杂度O(n^3)

代码(还在写)

T3 取数 50/85

题意

1到n个自然数中选k个自然数要求两两不相邻,问有多少种方法,模m

eg(1 3 5 )

又是一道打表规律题,正常解法dp可以通过前缀和优化到O(N* K)。另外我们可以重新定义F[I,J]表示从1到I中选择J个不连续数的方案数。通过考虑I选还是不选来进行状态转移。

1.如果不选I:则方案数为F[I-1,J];

2.如果选I:由于不能选相邻两个数,所以I-1不能选,则剩余的J-1个数只能在1到I-2中选,即F[I-2,J-1];

f[i][j]=f[i-1][j]+f[i-2][j-1] (j>1)

f[i][j]=1 (j==1) 边界条件

然而这样写只有70分

考虑找规律

过打表可以发现规律,当然我们也可以这样来考虑,为了保证所选K个数不连续,我们可以考虑先从N-(K-1)个数中选择K个数出来,这样选出来的是不能保证不连续的,但我们可以把该方案调整成合法方案,只要把第I(1<=I<=K)个数每个数加I-1,这样每个方案就一一对应于一个合法方案。所以答案为C(N-K+1,K)。如题目中样例,我们可以认为是C(4,3),从1到4中选3个数的方案跟从1到6中选3个不连续的方案是一一对应的,用上面的方法得到以下对应关系:

(1,2,3)<-> (1,3,5)

(1,2,4)<-> (1,3,6)

(1,3,4)<-> (1,4,6)

(2,3,4)<-> (2,4,6)

注意85%的数据N<=1000000,M=1000003,根据上面分析,答案Ans=(N-K+1)* (N-K) * ...* (N-2 * K+2)/(K * (K-1) ... 1 )mod M,我们可以先把分子即(N-K+1) * (N-K) * ...* (N-2* K+2)mod M的值计算出来记为a,同样把分母即K(K-1) ... 1 mod M的值计算出来记为b,由于这里M=1000003是一个素数,所以Ans的答案是唯一的。我们可以枚举Ans再判断Ans b mod M=a是否成立即可。时间复杂度为O(N+M),预计得分85分。

代码

#include<bits/stdc++.h>
using namespace std;
long long a=1,b=1,n,k,m;
int main(){
	scanf("%lld %lld %lld",&n,&k,&m);
	for(int i=1;i<=k;++i){
		a=a*(n-2*k+i+1)%m;
		b=(b*i)%m;
	}
	for(int ans=1;ans<m;++ans){
		if(((ans*b)%m)==a){
			printf("%d",ans);
			return 0;
		}
	}
	return 0;
}

正解

计算C(N-K+1,K)mod M,跟方法三同样先计算出分子分母对M的余数a和b,根据x=(x/y)* y可知:a=(Ansb)mod m,再转化成Ans b+m* P=a,其中b,m和a为已知,并且m为素数,这样就变成我们熟悉的a* x+b* y=c,用扩展GCD来求。时间复杂度为O(N)。预计得分:100分。

代码(咕咕)

T4 逃离洞穴 100/40

题意

嘤嘤嘤,这题本来能ac的就差一点点啊,忘记判断有人的才取最大值了。。。
spfa还没死

代码

#include<bits/stdc++.h>
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int N=510,M=100010;
int head[N],ver[M],edge[M],Next[M],d1[N],d2[N],d[N],op[N];
int n,m,k,T,tot,maxn;
queue<int> q;
bool v[N];
long long ans;
void add(int x,int y,int z){
	ver[++tot]=y;edge[tot]=z;Next[tot]=head[x],head[x]=tot;
}
void spfa(){
	memset(d1,0x3f,sizeof(d1));
	d1[1]=0,v[1]=1;
	q.push(1);
	while(q.size()){
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i],z=edge[i];
			if(d1[y]>d1[x]+z){
				d1[y]=d1[x]+z;
				if(!v[y]) q.push(y),v[y]=1;
			}
		}
	}
	memset(d2,0x3f,sizeof(d2));
	memset(v,0,sizeof(v));
	d2[n]=0,v[n]=1;
	q.push(n);
	while(q.size()){
		int x=q.front();q.pop();
		v[x]=0;
		for(int i=head[x];i;i=Next[i]){
			int y=ver[i],z=edge[i];
			if(d2[y]>d2[x]+z){
				d2[y]=d2[x]+z;
				if(!v[y]) q.push(y),v[y]=1;
			}
		}
	}
	for(int i=1;i<=n;++i){
		d[i]=min(d1[i],d2[i]);
	}
}
int main(){
	freopen("escape.in","r",stdin);
	freopen("escape.out","w",stdout);
	n=read();m=read();T=read();
	for(int i=1;i<=m;++i){
		int x,y,z;
		x=read();y=read();z=read();
		add(x,y,z);
		add(y,x,z);
	}
	spfa();
	k=read();
	int tmp;
	for(int i=1;i<=k;++i){
		tmp=read();
		op[tmp]++;
	}
	for(int i=1;i<=n;++i){
		if(d[i]<=T)  ans+=op[i];
		if(op[i]) maxn=max(maxn,min(d1[i],d2[i]));
	}
	printf("%lld\n%d",ans,maxn);
	return 0;
}
/*
4 4 3
1 2 5
2 4 3
1 3 4
3 4 6
4
1 2 3 4
*/
posted @ 2019-08-26 21:11  End_donkey  阅读(127)  评论(0编辑  收藏  举报