2018提高组模拟9

2018提高组模拟9

—————————————————————————————————————————20181004

T1

K进制

(WOJ4036)

【模拟||数论】

描述

给定一个K(2<=K<=16)进制数a,判断a是否能被K-1整除。

输入

第一行是一个整数t(1<=t<=50),表示测试点数量。

对于每组数据,第一行一个整数K,表示进制。

第二行一个K进制数,表示a。保证a是合法的K进制数,没有前导0,且只由’0’-‘9’、’A’-‘F’构成。

输出

如果a可以被K-1整除,输出”yes”,否则输出”no”。

样例输入

2
16
2D
10
19

样例输出

yes
no

提示

对于40%的数据,a的长度不超过5。

对于100%的数据,a的长度不超过100000。

题解

像十进制的除法的竖式一样模拟就好了

当然还有一种神奇做法:

#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
int t,k;
char c[100010];
void solve(){
	int len=strlen(c+1),now=0;
	for(int i=1;i<=len;++i){
		now*=k;
		if(isdigit(c[i]))now+=(c[i]^48);
		else now+=(c[i]-'A'+10);
		now%=(k-1);
	}
	if(!now)printf("yes\n");
	else printf("no\n");
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&k);
		scanf("%s",c+1);
		solve();
	}
	return 0;
}

T2

(WOJ4037)

 排队

 

描述

在成都某中学有m个男生与n个女生排队,这个学校的女生比较古怪,从某个位置(包含这个位置)开始往前数,男生的数量超过了女生的数量,女生会感觉不安全,于是会大叫起来,为了构建和谐校园,安排队伍时应该避免这样的情况。请你计算出不会引发尖叫的排队方案的概率。(排队方案不同定义:当且仅当某个某个位置人不一样,如男生A、男生B ,与男生B、男生A ,2个排列是不同方案)

输入

第一行1个整数, 表示测试数据的组数。

每个数据 有两个数 N,M(N个女生,M个男生)

输出

对于每组数据,输出一个实数(保留到小数点后 6 位)

样例输入

3
1 0
0 1
1 1

样例输出

1.000000
0.000000
0.500000

提示

【 Hint】

第一组:只有一个女生,一种方案且可行

第二组:只有1个男生,一种方案且不行

第三组:两种方案 女、男可行,男、女不可行,可行概率0.5

【数据规模】

30%的数据: (测试组数<=10),(0<=N,M<=1000).

100%的数据: (测试组数=9008 ), ( 0<=N,M<=20000 ).

题解

数学找规律

先打一个暴利程序,然后小数据输入看结果,很有规律的!!

暴力程序:

#include<cstdio>
#include<iostream>
using namespace std;
inline int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
int t,a,b;
int dfs(int now,int g,int bo){
	if(bo>g)return 0;
	if(now>a+b)return 1;
	int ans=0;
	if(g<a)ans+=dfs(now+1,g+1,bo);
	if(bo<b)ans+=dfs(now+1,g,bo+1);
	return ans;
}
int bb(int x){
	if(x==1)return 1;
	if(x<1)return 1;
	return x*bb(x-1);
}
int main(){
	t=read();
	while(t--){
		a=read();b=read();// 女   男 
		if(b>a){
			printf("0.000000\n");
			continue;
		}
		printf("%lf\n",1ll*dfs(1,0,0)*bb(a)*bb(b)*1.000000/bb(a+b));
	}
	return 0;
}

正解(转换方式很多)

  可以将原问题转化一下,看成是在一个二维平面上行走,女生看成移动(1,0),男生看成移动(0,1),

那么到达(N,M)点且路线又不走到y=x 这条直线上方的路线总数就是答案,

这个组合问题很经典,方案数为 C(M+N,M)-(M+N,M-1),所以可以知道答案就是1-M/(N+1) 

#include<cstdio>
#include<iostream>
using namespace std;
inline int read(){
	int x=0;char c=getchar();
	while(!isdigit(c))c=getchar();
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x;
}
int t,a,b;
int main(){
	t=read();
	while(t--){
		a=read();b=read();// 女   男 
		if(b>a||(b==0&&a==0)){
			printf("0.000000\n");
			continue;
		}
		printf("%.6lf\n",(double)(a-b+1)/(double)(a+1));
	}
	return 0;
}

 

T3

(WOJ4038)

(tarjan无向图边双连通分量+树形DP)

航班

 

描述

L因为业务繁忙,经常会到处出差。因为他是航空公司的优质客户,于是某个航空公司给了他一个优惠券。

他可以利用这个优惠券在任何一个国家内的任意城市间免费旅行,当他的路线跨国才会产生费用。L有一个航空公司的价格表与航线。而且每个城市出发都能到所有的城市,2个城市间可能有不止一个航班,一个国家内的2个城市间一定有不同的路线,但是不同国家的城市间只有一条路线。L想知道从每个城市出发到产生费用最多的城市,不过你不能重复在一个航班上飞来飞去产生费用,必须沿最少的费用路线飞行

输入

第一行,两个整数 N,M,表示N 个城市, M 条航线。

接下来 M 行,每行三个整数 a,b,c,表示城市 a,b 之间有一条费用为 c 的航线。

输出

共 N 行,第 i 行为从城市 i 出发到达每个城市额外费用的最大值。

样例输入

6 6
1 4 2
1 2 6
2 5 3
2 3 7
6 3 4
3 1 8

样例输出

4
4
4
6
7
7

提示

【解释】

有四个国家,包含的城市分别为 {1,2,3},{4},{5},{6}。

从城市 1 出发到达城市 6,乘坐(1,3)(3,6)两个航班费用最大,(1,3)在国内为免费航班, (3,6)的费用为 4,所以从 1 出发的最大费用为 4。

【数据规模】

对于 40%的数据 1<=N<=1000,1<=M<=1000

对于 100%的数据 1<=N<=20000,1<=M<=200000

题解

先用无向图边双连通分量缩点,再用一个树形DP就好了

方案一:

两次dfs/树形dp求直径的2个端点A,B,

则 x到最远距离的距离是 MAX(dis(x,A),dis(x,B)) 

方案二:

树形DP换根法

记录每一个点向下的最大值与次大值

对于每一个点,需求的最大答案就是:

从父亲走到自己的兄弟,或从父亲走到自己的祖父,或由自己走到自己的儿孙

缩点,重新建边后,不一定是树

因为两个国家中电的多个城市可能与另一个国家的多个城市都有边!!!!!!!!!!!!!!!!!

不过,vis数组可以解决…………

~~~~(>_<)~~~~ ~~~~(>_<)~~~~ ~~~~(>_<)~~~~ 

#include<cstdio>
#include<iostream>
#include<stack>
#include<cstring>
#include<queue>
using namespace std;
inline int read(){
	int x=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
	while(isdigit(c)){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
struct edge{
	int u,v,w,nxt;
}e[400010],a[400010];
int first[20010],first2[20010],cnt=1,cn=0;
inline void add(int u,int v,int w){
	e[++cnt].u=u;e[cnt].v=v;e[cnt].w=w;
	e[cnt].nxt=first[u];first[u]=cnt;
} 
inline void add2(int u,int v,int w){
	a[++cn].u=u;a[cn].v=v;a[cn].w=w;
	a[cn].nxt=first2[u];first2[u]=cn;
}
int n,m;
int dfn[20010],low[20010],tot=0,vis[20010],dcc[20010],siz=0;
stack<int>s;
void tarjan(int x,int in){
	dfn[x]=low[x]=++tot;
	s.push(x);vis[x]=1;
	for(int i=first[x];i;i=e[i].nxt){
		int y=e[i].v;
		if(!dfn[y]){
			tarjan(y,i);
			low[x]=min(low[x],low[y]);
		}
		else if(i!=(in^1)&&vis[y]){//
			low[x]=min(low[x],dfn[y]);
		}
	}
	if(dfn[x]==low[x]){
		++siz;
		while(1){
			int u=s.top();s.pop();
			dcc[u]=siz;vis[u]=0;
			if(u==x)break;
		}
	}
}
int dis1[20010],dis2[20010],mx=0,roota,rootb,vis1[20010],vis2[20010];
void dfs1(int x,int fa){
	vis1[x]=1;
	for(int i=first2[x];i;i=a[i].nxt){
		int y=a[i].v;
		if(y==fa||vis1[y])continue;
		dis1[y]=dis1[x]+a[i].w;
		if(dis1[y]>mx){
			mx=dis1[y];
			roota=y;
		}
		dfs1(y,x);
	}
}
void dfs2(int x,int fa){
	vis1[x]=1;
	for(int i=first2[x];i;i=a[i].nxt){
		int y=a[i].v;
		if(y==fa||vis1[y])continue;
		dis1[y]=dis1[x]+a[i].w;
		if(dis1[y]>mx){
			mx=dis1[y];
			rootb=y;
		}
		dfs2(y,x);
	}
}
void dfs3(int x,int fa){
	vis2[x]=1;
	for(int i=first2[x];i;i=a[i].nxt){
		int y=a[i].v;
		if(y==fa||vis2[y])continue;
		dis2[y]=dis2[x]+a[i].w;
		dfs3(y,x);
	}
}
int ans[20010];
int main(){
	n=read();m=read();
	for(int i=1,u,v,w;i<=m;i++){
		u=read();v=read();w=read();
		add(u,v,w);add(v,u,w);
	}
	tarjan(1,0);
	for(int i=2,u,v;i<=cnt;i++){
		u=e[i].u;v=e[i].v;
		if(dcc[u]==dcc[v])continue;
		else add2(dcc[u],dcc[v],e[i].w),add2(dcc[v],dcc[u],e[i].w);
	}
	dfs1(1,0);
	mx=0;dis1[roota]=0;
	memset(vis1,0,sizeof(vis1));
	dfs2(roota,0);
	dfs3(rootb,0);
	for(int i=1;i<=siz;i++)
		dis1[i]>dis2[i]?ans[i]=dis1[i]:ans[i]=dis2[i]; 
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[dcc[i]]);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值