9.3模拟赛

T1
数字三角形
题目描述:
让人怀念的数字三角形。
给出一个如下的数字三角形:
1
1 2 1
1 2 3 2 1
1 2 3 4 3 2 1
……
你从三角形的顶部出发,假如右边有数,则移动到右边相邻的数上,否则,移动到下一行第一个数上,沿途取走所有经过的数,现在多次询问,若要使取走的数的和大于等于N,则至少需要经过多少个数?
输入:
第一行输入T,代表询问的次数,之后T行输入每次询问的N。
输出:
输出T行,每行表示询问的答案
样例输入:
5
6
9
11
21
35
样例输出:
5
7
7
13
19
数据规模:
20%:T=1,N<=1000
40%:T<=10,N<=10000
100:T<=100000,N<=10^18

根据题目的引导,我们可以先确定要取到哪一行,再去确定在那一行的哪一列。我们通过观察可以发现每一行的数值和是n^2.
可以通过预处理出行的前缀和。对于每一个询问二分到那一行。先通过判断在行的前半段还是后半段,再进行二分或者暴力。

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[2000100];
int mjc[2000100];
int k,n;
int tmp;
signed main(){
	a[1]=1;
	int t=1;
	tmp=1;
	while(1){
		t++;
		a[t]=a[t-1]+t*t;
		tmp+=(2*t-1);
		if(a[t]>=1e18) break;		
	}
	for(int i=1;i<=1600000;i++){
		mjc[i]=mjc[i-1]+i;
	}
	scanf("%lld",&k);	 
	while(k--){
		scanf("%lld",&n);
		int pos=lower_bound(a+1,a+t+1,n)-a;
		int now=n-a[pos-1];
		int ans=(pos-1)*(pos-1);
		int p=0;
		if(now>pos*(pos+1)/2){
			now-=pos*(pos+1)/2;
			ans+=pos;
			for(int i=pos-1;i>=1;i--){
				now-=i;
				ans++;	
				if(now<=0){
					break;
				}
			}
		}
//		int p=0;
//		for(int j=1;j<=pos;j++){
//			p+=j;
//			ans++;
//			if(p>=now) break;
//		}
		else{
			p=lower_bound(mjc+1,mjc+1600000+1,now)-mjc;
		}
		printf("%lld\n",ans+p);
	}
}

反思:这个题目做的时候虽然想到了对拍也对拍了但是还是没有发现错误。做题目的时候不够仔细,不要看到题目简单就想快点做完,也不要指望对拍,重要的是在第一遍写的时候就要注意到该注意的细节。

T2
跳跃滴球
题目描述:
给你M个球,N个盒子,球与球,盒子与盒子之间互不相同,现在每个球都有预先指定的可以容纳它的两个盒子,你可以将这个球放进两个盒子中的任意一个。 现在,我们要求把所有的球放进盒子里,并且满足每个盒子里最多只有一个球,请求出有多少种满足要求的方案。
输入:
第一行输入两个数M,N,分别表示总共有M个球以及N个盒子
接下来输入M行,每行有两个整数x,y,( 0<=x,y<N ),表示该球所对应的两个盒子的编号,x可能等于y,这代表这个球只能被放在一个盒子里。
输出:
输出方案数对1 000 000 007 取模的结果,如果不存在一种合法方案,则输出0.
样例输入1:
4 5
0 1
1 2
3 4
4 3
样例输出1:
6
样例输入2:
3 2
0 0
1 1
0 1
样例输出2:
0
数据规模:
20%:N<=100,M<=15
40%:N<=1000,M<=1000
100:N<=100000,M<=100000

要想到一个球可以放两个盒子就考虑到连边。这样整个题就变成一个图。答案就是每一部分连通图贡献的乘积。每一个连通图的变数就是要取的数,点就是容器的个数。一棵N个节点的树,选法是N.若有且仅有一个环,如果是自环就是1,不是就是2.
如果边数大于点数就为0;

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int fa[100010];
int find(int x){
	if(fa[x]==x) return fa[x];
	return fa[x]=find(fa[x]);
}
int sn[100010],sm[100010];
bool vis[100010],h[100010];
int n,m;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<m;i++){
		fa[i]=i;
		sn[i]=1;
		sm[i]=0;
	}
	for(int i=1;i<=n;i++){
		int x,y;
		scanf("%d%d",&x,&y);
		int eu=find(x);
		int ev=find(y);
		if(x==y){
			sm[eu]++;
			vis[eu]=1;
			continue;
		}
		if(eu==ev){
			sm[eu]++;
			continue;
		}
		fa[eu]=ev;
		sm[ev]+=sm[eu]+1;
		sn[ev]+=sn[eu];
		if(vis[eu]) vis[ev]=1;
	}
	int ans=1;
	for(int i=0;i<m;i++){
		int x=find(i);
		if(h[x]) continue;
		h[x]=1;
		if(sm[x]>sn[x]){
			ans=0;
			continue;
		}
		if(sm[x]==sn[x]-1){
			ans=(1ll*sn[x]*ans)%mod;
			continue;
		}
		if(vis[x]) continue;
		ans=(ans*2ll)%mod;
	}
	printf("%d\n",ans); 
}

T3
Ex
题目描述:
给你一个原始的运算式,它由正整数和三种位运算符组成(&,|,^),比如说1&2|
3,或者1|2|3,其中三种位运算符的优先级是一样的。现在呢,这个原始的运算式里的运算符有一定概率与它右边相邻的数字一起消失,比如说1&2|3,假设其中第2个位置的&有0.5的概率和第3个位置的2一起消失,第4个位置的|有0.4的概率和第5个位置的3一起消失,那么总共就有4种可能,第一种,只剩一个1 = 1,概率P1=0.50.4=0.2,第二种,剩下1&2 =0,概率P2=(1-0.5)0.4=0.2,第三种,剩下1|3 =3,概率P3=0.5(1-0.4)=0.3,第四种,剩下1&2|3 = 3,概率P4=(1-0.5)(1-0.4)=0.3,故期望得到的运算式的结果为P11+P20+P33+P43=2。
现在,给你这样一个运算式,以及运算式中每个位置上的运算符与它右边相邻的整数一起消失的概率,请求出它期望得到的结果是多少。
输入:
第一行为一个正整数n ( 0 < n <= 233 )
第二行为这个式子里从左往右的n+1个整数Ai(其中Ai小于2^20),
第三行为该式子中从左往右的n个位运算符,
第四行为n个0到1之间的浮点数,分别表示每个位置上的运算符与它右边相邻的整数一起消失的概率
输出:
输出期望值结果,保留到小数点后4位。
样例输入1:
2
2 3 5
^ ^
0.1 0.2
样例输出1:
3.6600
样例输入2:
2
1 4 11
^ ^
0.5 0.5
样例输出2:
7.5000

考试的时候打了暴力。正解是dp.题目中有一个细节,ai<=2^20。这其实是在提示我们把每一个数拆开。考试没有做的原因是因为脑子里没这种想法,以后要对这种描述更加敏感。
对于每一位分解,分别进行dp.这种dp的优势在于它将期望转换为概率,这样就将问题简化。b[i]表示在当前位dp的时候,这个单元这一位为1的概率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值