2020.02.22【NOIP提高组】模拟B 组9反思

我又一次被暴虐了(虽然每次都这么说),这次讲完题后发现自己的想法都是可以(AC的,上天给我的AK的机会我没有珍惜)。在T1这死磕了很久,用了两个小时,短短的代码但是我还是调不出来。(唉,实现能力太弱)T2把题目理解错了(本时一道水题),T3想到可AC的方法(但不是正解)但是不会打C++里的vector唉,我怎么就不能成为一名巨佬啊!下一次一定要合理分配时间,否则会酿成大错。
分数:100,Rank.9
T1:
题目大意
奖励卡
小 X 喜欢程序比赛,广受称赞的冠军联赛总决赛将在他的家乡举行,他当 然也想参加这次盛大的比赛。虽然这个比赛很风靡,但大赛的大多数门票还是要 留给 VIP 们和赞助商们。
公众售票按如下规则分发:
想看比赛的观众需要提交他们的“请求”, “请求”上必须说明他们想要用来 购买门票的方式。假如这里还有 n 张门票。票将一轮一轮发放,每一轮中,每个 还没有被满足的请求会根据所提供的购票方式不同,而获得不同的“得票机会”。
然后其中的一个“得票机会”会被随机抽取,该得票机会对应的请求会得到 一张门票,并且该“请求”不再参与之后的发票。n 轮结束后,或者所有请求被 满足后,售票停止。
使用 I 卡付费的观众拥有两个“得票机会”,而其他卡(比如:A 卡)付费 则只能拥有一个“得票机会”。
小 X 有一张 I 卡,也有一张 A 卡。
他的兄弟小 Y 在一家负责发票的公司工作,所以他能提前告诉小 X 有多少 人用 I 卡付费,多少人用其他方式付费。现在小 X 想知道如果他用 I 卡或者 A 卡付费他能得到一张票的可能性,这样他能做出合理的选择。
注意他个人的支付方式还没有被小 Y 计算在内,求帮忙!
输入
第 1 行:座位数 n,I 卡支付请求数 a,其他方式付费请求数 b。
输出
第 1 行:用 I 卡付费的得票可能性。
第 2 行:用 A 卡付费的得票可能性。
答案误差不得超过 10^-9。
样例输入1
1 1 2
样例输出2
0.3333333333333333
0.2
样例输入1
10 10 10
样例输出2
0.5870875690480144
0.3640355515319861
数据范围
对于 10%的数据,n<=2。
对于 20%的数据,n<=3。
对于 30%的数据,n<=4,a,b<=4。
对于 60%的数据,n<=3000,a,b<=2000。
对于 100%的数据,1<=n<=3000,0<=a,b<=10^9。
题解
对于这道让我磕了这么久的题,我一定要好好对待
很明显,这是一道概率dp(哦不,我不能装巨佬) 。
设f[i][j][0/1]代表有i个用I卡得到机会,有J个用A卡得到机会,小x用0/1(0代表小x用I卡,1代表小x用其他卡)
我们倒着来转移
那么状态转移方程就是:

f[x][y][0]=(db)2/(db)(c+2)+(db)(a-x)*(db)2/(db)(c+2)*f[x+1][y][0]+(db)(b-y)/(db)(c+2)f[x][y+1][0];
f[x][y][1]=(db)1/(db)(c+1)+(db)(a-x)
(db)2/(db)(c+1)*f[x+1][1][1]+(db)(b-y)/(db)(c+2)*f[x][y+1][1];

db是double
期望得分0,实际得分0
Code

#include<cstdio>
#define db double
#define ll long long
using namespace std;
ll n,a,b;
db f[3005][3005][2];
void dfs(int x,int y) {
	if(x>a||x+y>=n||y>b)return;
	if(f[x][y][0]>0.0)return;
	dfs(x+1,y);
	dfs(x,y+1);
	ll c=(a-x)*2+b-y;
	f[x][y][0]=(db)2/(db)(c+2)+(db)(a-x)*(db)2/(db)(c+2)*f[x+1][y][0]+(db)(b-y)/(db)(c+2)*f[x][y+1][0];
	f[x][y][1]=(db)1/(db)(c+1)+(db)(a-x)*(db)2/(db)(c+1)*f[x+1][1][1]+(db)(b-y)/(db)(c+2)*f[x][y+1][1];
}
int main() {
	scanf("%lld%lld%lld",&n,&a,&b);
	dfs(0,0);
	printf("%0.12lf\n%0.12lf",f[0][0][0],f[0][0][1]);
}

T2
假期计划
题目大意
航空公司开设了连接着 N 个城市的航班。像任何航线一样,这些城市中的 K 个被设为枢纽。
现在,航空公司提供 M 个单行航班,其中航班 i 从城市 u_i 到城市 v_i 并花 费 d_i 美元。像任何明智的航线一样,对于每一个航班,u_i 和 v_i 中至少一个是 枢纽。两个城市间最多有一个直飞航班,并且没有航班起点与终点为同一城市。
小 X 负责为航空公司运营票务,他收到了 Q 个学生假期的单行航班的请求, 其中第 i 个请求是从城市 a_i 到另一个城市 b_i。
由于小 X 被处理这些票的工作淹没,请帮他计算每个购票请求能否被实现, 以及如果能实现时它的最小花费。
为了减小输出大小,你只要输出可能的购票请求的总数,以及它们总花费的 最小值。
输入
第 1 行:N,M,K 和 Q。
第 2…1+M 行:第 i+1 行包含 u_i,v_i 和 d_i。
第 2+M…1+M+K 行:每行包含一个中枢的编号。
第 M+K+2…M+K+Q+1:每行两个数,表示从 a_i 到 b_i 的购票请求。
输出
第 1 行:能实现的购票请求数。
第 2 行:能实现的购票请求的最小总花费。
样例输入
3 3 1 2
1 2 10
2 3 10
2 1 5
2
1 3
3 1
样例输出
1
20
数据范围
对于 30%的数据,N<=100,M<=2,000。
对于 100%的数据,1<=N,M<=20,000,1<=K<=200,1<=Q<=50,000, 1<=d_i<=10,000。
题解
我们先看数据范围,k很小,那肯定就是要让我们来用k嘛,所以经过一大波心里波折,可以得到一个思路:
我们就把每一个枢纽都给他跑一遍最短路,求出它到其他点的最短距离。对于每一个询问x,y,就看一下x是否为枢纽,如果是,则把x到y的最短路加入答案,否则找一个x能直接到达的枢纽且是到达y点最近的,用它统计答案。
就这样,这道题变成了水题。
可惜,我连水题都不会做。
期望得分:30,实际得分:40

#include<cstdio>
#include<cstring>
#include<queue>
#define fo(x,a,b) for(register int x=a;x<=b;x++)
#define ll long long
using namespace std;
int n,m,k,q,head[20001],cnt=0,a[20001],ans;
inline int read() {
	int s=0;
	char ch=getchar();
	while(ch<'0'||ch>'9')ch=getchar();
	while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
	return s; 
}
struct node {
	int to,next,value;
}edge[20001];
inline void add(int x,int y,int z) {
	edge[cnt].to=y;
	edge[cnt].value=z;
	edge[cnt].next=head[x];
	head[x]=cnt++;
}
bool vis[20001];
queue <int> d;
long long dis[201][20001],sum=0;
inline void spfa(int x,int y) {
	fo(j,1,n)dis[x][j]=0x3f3f3f3f,vis[j]=0;
	d.push(y);vis[y]=1;dis[x][y]=0;
	while(!d.empty()) {
		int now=d.front();
		d.pop();
		for(register int j=head[now];j!=-1;j=edge[j].next) {
			int to=edge[j].to;
			if(dis[x][now]+edge[j].value<dis[x][to]) {
				dis[x][to]=dis[x][now]+edge[j].value;
				if(!vis[to]) {
					vis[to]=1;
					d.push(to);
				}
			}
		}
		vis[now]=0;
	}
}
inline void find(ll c) {
	if(c!=0x3f3f3f3f) {
		ans++;
		sum+=c;
	}
}
signed int main() {
	n=read();m=read();k=read();q=read();
	memset(head,-1,sizeof(head));
	fo(i,1,m) {
		int u=read(),v=read(),w=read();
		add(u,v,w);
	}
	fo(i,1,k) {
		int u=read();
		a[u]=i;
		spfa(i,u);
	}
	fo(i,1,q) {
		int u=read(),v=read();
		if(a[u])find(dis[a[u]][v]);
		else {
			int t=0x3f3f3f3f;
			for(register int j=head[u];j!=-1;j=edge[j].next) {
				int to=edge[j].to;
				if(a[to]&&edge[j].value+dis[a[to]][v]<t)
					t=edge[j].value+dis[a[to]][v];
			}
			find(t);
		}
	}
	printf("%d\n%lld",ans,sum);
}

T3:
IQ测试
题目大意
PTY进行IQ测试,测试的项目是判断一个序列是否是另外一个序列删除若干个数字之后得
到的,PTY 深知自己的IQ 低于sqrt(-1),所以他请来了智商超高的你来替他解决问题。
输入
第一行为一个整数n,第二行包括n个用空格分开的整数ai,组成了最初的序列,第三行为一个整数m,表示n个IQ测试询问的序列,每个序列两行,第一行给出长度 L(1<=L<=n),然后下一行为L 个由空格分开的整数bi。
输出
共m行,如果询问的序列确实是由最初的序列删除一些数得到,就输出TAK,否则输出NIE。
样例输入
7
1 5 4 5 7 8 6
4
5
1 5 5 8 6
3
2 2 2
3
5 7 8
4
1 5 7 4
样例输出
TAK
NIE
TAK
NIE
数据范围
对于30%的数据:n<=1000,m<=1000
对于100%的数据:1<=ai,bi<=1000000,ΣL<=1000000,n,m<=1000000
题解
大水法可以过!!!!
用一个vector存一个数x出现的位置,对于每一个询问,都二分出一个位置,这个位置大于上一个数的位置,如果能找到,则输出TAK,否则NIE。
期望得分:30,实际得分:60
Code

#include<cstdio>
#include<cstring> 
#include<vector>
using namespace std;
int n,cnt=0,m;
vector <int> a[1000001];
int main() {
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		int x;
		scanf("%d",&x);
		a[x].push_back(i);
	}
	scanf("%d",&m);
	while(m--) {
		scanf("%d",&n);
		int last=0;
		bool bz=0;
		for(int i=1;i<=n;i++) {
			int x;
			scanf("%d",&x);
			if(!bz) {
				if(a[x].size()==0) {
					bz=1;
					continue;
				}
				int ret=-1,l=0,r=a[x].size()-1;
				while(l<=r) {
					int mid=(l+r)>>1;
					if(a[x][mid]>last)ret=mid,r=mid-1;
					else l=mid+1;
				}
				if(ret>=0)last=a[x][ret];
				else bz=1;
			}
		}
		if(!bz)printf("TAK\n");
		else printf("NIE\n");
	}
}

下次一定要加油加油再加油!

猜你喜欢

转载自blog.csdn.net/MZHjr/article/details/104468357