20170711考试总结

本文解析了几道算法竞赛题目,包括游戏得分最大化问题、快递配对问题及集合子集排列问题等,提供了完整的代码实现和思路分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

隔膜

【问题描述】

steam 夏季大促销来啦,azui 大爷最近在 steam 上买了 1mol 的游

戏。一天他突然发现了一个搬砖的游戏:种砖头,每种砖头有 mi 个,每一个的价值为 di。每一个单位时间你必须搬一块砖,到无砖可搬为止。有一个得分系数 F,初始时 1。搬一块砖的得分为当时的得分系数 F*di。有个时间分割点。每过一个时间分割点,F 会自己加一。例如在时间 pi 的得分为 i*di,而在时间 pi+1 的得分为(i+1)*di。azui 大爷觉得这个游戏 too simple,不想去玩,于是让你去帮他上分,希望你能告诉他每局游戏的最大的分。你一定知道这么简单的题目怎么做,快帮帮 azui 大爷吧。

【输入格式】

第一行一个数 N。接下来的行,每行两个数,表示 mi  di之后的一行一个数 T。接下来的一行个数,pi

【样例输入 1】

2

3 8

5 10

1

20

【样例输出 1】

74

【样例输入 2】

1

1 1000

1

1

【样例输出 2】

1000 

【数据范围】

对于 30%的数据,

1<=N<=10

对于 100%的数据,

1<=N<=100

1<=mi<=109

0<=di<=1000

1<=t<=1001<=p2<p3<…<pt<=1012

题解:按d排序。

成绩:AC

分析:水题。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
const int N=100+10;
struct node{
	long long num,val;
	bool operator < (const node &x)const{
		return val<x.val;
	}
}arr[N];
int n,T,now=1;
long long p,f,ans,res;
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld %lld",&arr[i].num,&arr[i].val);
	sort(arr+1,arr+n+1);
	scanf("%d",&T);
	while(T--){
		scanf("%lld",&p);
		f++;
		for(;now<=n;now++){
			res+=arr[now].num;
			ans+=arr[now].num*arr[now].val*f;
			if(res>=p) break ;
		}
		if(res>p){
			arr[now].num=res-p;res=p;
			ans-=arr[now].num*arr[now].val*f;
		}
		else	now++;
	}
	for(;now<=n;now++)
		ans+=arr[now].num*arr[now].val*(f+1);
	printf("%lld\n",ans);
}


快递配对

【问题描述】

azui  大爷厌倦了每天在家颓废的生活,于是开始打工送快递。Jeremy 同学不想让 azui 大爷太轻松,于是想让他送快递的路程尽可能的长。一句话来说就是:给出一棵个点的树,将这个点两两配对,求所有可行的方案中配对两点间的距离的总和最大为多少。

【输入格式】

一个数 n(1<=n<=100,000,n 保证为偶数)接下来 n-1 行每行三个数 x,y,z 表示有一条长度为的边连接y(0<=z<=1,000,000,000)

【输出格式】

一个数表示答案。

【样例输入】

6

1 2 1

1 3 1

1 4 1

3 5 1

4 6 1


题解:找树的重心,让每一条路都经过树的重心。因为假设树的根是确定的,最佳方案一定是每个节点都越过根去找配对节点,因为要让每个节点越过根,去掉根后子树一定要尽量平均,即树的重心。

成绩:0

分析:文件输入输出没有打.in和.out(被自己zz掉/(ㄒoㄒ)/~~),考试时想到了平均,但树的重心的概念完全忘了( ˇˍˇ ),就乱搞了一个点作根。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<stack>
#include<algorithm>
#include<vector>
using namespace std;
const int N=100000+10;
const int inf=0x3f3f3f3f;
void getint(int&son){
    char c;int flag=1;son=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9'){son=son*10+c-48;c=getchar();}
    son*=flag;
}
struct bian{
    int v,w,next;
}arr[N<<1];
int n,a,b,cnt,s,t,Min=inf,fir[N],dp[N],son[N];
long long w,dis[N],ans;
bool vis[N];
void link(int a,int b,int w){
    arr[++cnt].v=a,arr[cnt].w=w;
	arr[cnt].next=fir[b],fir[b]=cnt;
}
void work(int x,int pre){
	son[x]=1;
	for(int i=fir[x];i;i=arr[i].next)
		if(arr[i].v!=pre)
			work(arr[i].v,x),son[x]+=son[arr[i].v];
}
void dfs(int x,int pre){
	for(int i=fir[x];i;i=arr[i].next)
		if(arr[i].v!=pre)
			dfs(arr[i].v,x),dp[x]=max(dp[x],son[arr[i].v]);
	dp[x]=max(dp[x],n-son[x]);
}
void Dfs(int x){
	vis[x]=1;
	for(int i=fir[x];i;i=arr[i].next)
		if(!vis[arr[i].v]){
			dis[arr[i].v]=dis[x]+arr[i].w;
			Dfs(arr[i].v);
		}
}
int main(){
	//freopen("pairing.in","r",stdin);
	//freopen("pairing.out","w",stdout);
    getint(n);
    for(int i=1;i<n;i++){
		scanf("%d %d %lld",&s,&t,&w);
		link(s,t,w),link(t,s,w);
	}
	work(1,-1),dfs(1,-1);
	for(int i=1;i<=n;i++)
		Min=min(Min,dp[i]);
    for(int i=1;i<=n;i++)
        if(dp[i]==Min){
			Dfs(i);break ;
		}
	for(int i=1;i<=n;i++)
		ans+=dis[i];
	printf("%lld\n",ans);
}



【问题描述】

azui大爷在quack大爷的带领下开始玩集合了,可是他太懒了,不想做quack大爷布置的作业题,便拿来给你做了:

S 集合中有n个不同的元素,我们从1-n标号。考虑S 的子集Si,j,将这些子集排成一个r行c列矩阵的样子。

其中第一行为S1,1,S1,2,…,S1,c,第二行为S2,1,S2,2,..,S2,c一直到第r行为Sr,1, Sr,2,…, Sr,c。

这些集合还满足对于在一行中左右相邻的两个集右,左侧是右侧

的子集,即Si,j∈Si,j+1。

这些集合还满足对于在一列中上下相邻的两个集合,上方是下方

的子集,即Si,j∈Si+1,j。

问对于S 的全部子集,有多少可能的情况排成上述的矩阵(每个子集可以重复使用),结果模109 +7 输出。

你一定知道这么简单的题目怎么做,快帮帮azui大爷吧。

【输入格式】

一行三个数n,r,c.

【输出格式】

      一个数表示答案。

【样例输入】

1 2 2

【样例输出】

6

【样例解释】

1

2

3

4

如上图标号后,有:1是2和3的子集,1,2,3都是4的子集。

用0表示空集,1表示元素1,从左到右是1、2、3、4的话,有以下6种情况

0000 0001 0011 0101 0111 1111

 

【数据范围】

对于10%的数据,r*c*n <= 16

对于20%的数据,r*c <= 16, n <= 100

对于另20%的数据,r=1, c<=1000000

对于另10%的数据,n=1

对于100%的数据,r, c <= 1000000, n <= 10^9s

题解:把集合中每个数分开考虑,最后再把n=1时的答案n次方(简直不敢相信考场上就差这一步,然而n=1只有一个点%>_<%),考虑只填0和x,那x一定在矩阵的右下部分,就可以吧问题转换为从左上到右下一共有多少种走法(一共走r+c步,c步向下 => C(n,m) )。

成绩:0

分析:这次的文件输入输出不光没有打in和out还顺手注释掉了。。。但改过来也只有n=1的10分,主要是思考时看到特殊数据一直在人为地强化它的特殊性,半暴力半玄学地把式子写出来后n一变就懵掉(时间不够又很慌qnq)。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
const int N=100+10;
const long long mod=1e9+7LL;
int n,r,c,ans,map[N][N];
int Max(int a,int b,int c){
	return max(a,max(b,c));
}
void dfs(int x,int y){
	int pos=c*(x-1)+y;
	if(pos>=r*c+1){
		ans++;return ;
	}
	for(int i=Max(1,map[x-1][y],map[x][y-1]);i<=n;i++){
		map[x][y]=i;
		if(y<c)	dfs(x,y+1);
		else	dfs(x+1,1);
	}
}
void gcd(long long a,long long b,long long &x,long long &y){
	if(!b)	x=1,y=0;
	else	gcd(b,a%b,y,x),y-=a/b*x;
}
int inv(int n) {
    if(n == 1) return 1;
    return (long long)(mod-mod/n)*inv(mod%n)%mod;
}
long long cal(long long n,long long m){
	if(m>n-m)	m=n-m;
	long long res=1,x;
	for(int i=1;i<=m;i++){
		res=(res*(n-i+1))%mod;
		x=inv(i);
		while(x<0)	x+=mod;
		res=(res*x)%mod;
	}
	return res;
}
long long qpow(long long tmp,int p){
	long long res=1LL;
	while(p){
		if(p&1)	res=(res*tmp)%mod;
		p>>=1,tmp=(tmp*tmp)%mod;
	}
	return res;
}
int main(){
	scanf("%d %d %d",&n,&r,&c);
	printf("%lld\n",qpow(cal(r+c,c),n));
	return 0;
}
/*
1 2 2
*/


总结:感觉开发了自己的潜能。。(一场考试第一次两次文件错误O__O"…),我记得交卷前检查了的( ⊙ o ⊙ ),然而没看出来,错了之后又看了一遍,还是没看出来%>_<%。很多以前学过的东西忘了(树的重心o(╯□╰)o)各种失误也挺多的。。/(ㄒoㄒ)/~~,(果然洛谷说我今天大凶还忌参加模拟赛TAT)衷心期待明天不要炸得这么难看o@_@o 2333333333333333333333333333333333333333333333333333333333

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值