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