2020ccpc秦皇岛

A. A Greeting from Qinhuangdao

题目大意:
从r个红气球,b个蓝气球中随机选两个球,两个都是红球的概率是多少?

思路:
概率即为:
r r + b × r − 1 r + b − 1 \frac{r}{r+b}\times \frac{r-1}{r+b-1} r+br×r+b1r1
注意当 r < 2 r<2 r<2时,直接输出 0 / 1 0/1 0/1
其他情况,输出最简化之后的结果即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd (ll a , ll b){
    return (b == 0) ? a : gcd(b,a%b);
}
int main(){
    int t;
    cin >> t;
    for(int i = 1;i <= t;i++){
        int r, b;
        cin >> r >> b;
        if(r < 2)
            printf("Case #%d: 0/1\n",i);
        else{
            ll zi = r*(r-1);
            ll mu = (b+r)*(b+r-1);
            ll y = gcd(zi,mu);
            printf("Case #%d: %lld/%lld\n",i,zi/y,mu/y);
        }
    }
    return 0;
}

G. Good Number

在区间 [ 1 , n ] 中 寻 找 满 足 [1,n]中寻找满足 [1,n] ⌊ x k ⌋ \lfloor \sqrt[k]{x} \rfloor kx 能够整除 x x x x x x个数

思路:
1、即寻找 x ⌊ x k ⌋ \frac{x}{\lfloor \sqrt[k]{x} \rfloor} kx x为整数的个数
x ⌊ x k ⌋ = a ( a 为 整 数 ) \frac{x}{\lfloor \sqrt[k]{x} \rfloor}=a(a为整数) kx x=aa
由于 ⌊ x k ⌋ \lfloor \sqrt[k]{x} \rfloor kx 对结果进行了向下取整,其实导致了很多数字开k次方后的结果都相同。
进一步可以发现区间 [ x k , ( x + 1 ) k − 1 ] [x^k, (x+1)^k-1] [xk,(x+1)k1]内的数,开k次方根再向下取整后所得结果都为x
如:
4,5,6,7,8,开平方根向下取整的结果都是2
区间 [ 4 , 8 ] [4,8] [4,8]中能整除以2的数的个数为:floor((8-4+1)/2)=3
(实用数学知识)
2、特判
当k=1时,满足条件的数的个数为n个;
当k不断变大,开k次方根的结果会不断变小
由于数据范围在1e9之内(即int),因此即使是1e9那么大的数字,开31次方根向下取整之后,都会趋于1。若区间 [ 1. n ] [1.n] [1.n]内的数开31次方,其向下取整之后的结果全为1,故满足条件的数的个数即为n

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=1e4+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
ll ksm(ll a,ll b){
	ll ans=1;
	while(b){
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
int main(){
	int t;
	scanf("%d",&t);
	for(int i=1;i<=t;i++){
		int n,k;
		scanf("%d%d",&n,&k);
		printf("Case #%d: ",i);
		ll ans=0;
		if(k==1||k>=31) ans=n;
		else{
			for(int j=1;;j++){
				int l=ksm(j,k);
				int r=ksm(j+1,k)-1;
				if(l<=n&&r>=n){
					if(l==n) ans++;
					else{
						r=n;
						ans+=(1+(r-l)/j);
					}
					break;
				}
				ans+=(1+(r-l)/j);
			}
		} 
		printf("%lld\n",ans);
	} 
	return 0;
}

F. Friendly Group

一个小组共有n个人,m对朋友关系。现需选派人去参加会议。小组的初始分值为0,若互为朋友关系的两个人都去参加会议,则小组分值加1分;若只有其中一个人参加会议,则小组分值减1。如果最终共有k个人去参加会议,则小组分值还要减k。求小组的最大分值是多少?

思路:
1、每加入一条边:分值加一
2、每加一个点:分值减一
3、若边数增加一条的同时,点数也增加一个,则分值不变。

对于互为朋友关系的两个人,他们都去参会,肯定比一个人去要优。
所以存在朋友关系的人,我们考虑将他们放在同一个集合中–>并查集
每个集合的分值即为:该集合中的边数-点数
最终分值为正的集合,则为我们要选择的集合,分值为负的集合,直接舍去。

#include<bits/stdc++.h>
#define pb push_back
#define mpi make_pair
#define fi first
#define se second
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define pi acos(-1)
using namespace std;
typedef long long ll;
const int maxn=3e5+10;
const ll mod=998244353;
const int INF=0x3f3f3f3f;
typedef pair<int,int> P;
typedef unsigned long long ull;
inline ll lowbit(ll x){return x&(-x);}
int fa[maxn];
int num[maxn];//记录点数
int sum[maxn];//记录边数
int find(int x){
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main(){
	int t;
	scanf("%d",&t);
	for(int tt=1;tt<=t;tt++){
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=1;i<=n;i++){
			fa[i]=i;
			num[i]=1;
			sum[i]=0;
		}
		while(m--){
			int x,y;
			scanf("%d%d",&x,&y);
			int fx=find(x);
			int fy=find(y);
			if(fx!=fy){
				fa[fx]=fy;
				num[fy]+=num[fx];
				sum[fy]+=(sum[fx]+1);
			}
			else {//若该边的两个端点,已在同一集合中,直接在加上这条边
				sum[fy]++;
			}
		}
		ll ans=0;
		for(int i=1;i<=n;i++){
			if(find(i)==i&&sum[i]-num[i]>0){
				ans+=(sum[i]-num[i]);
			}
		}
		printf("Case #%d: %lld\n",tt,ans);
	}
	
	return 0;
}

E. Exam Results

题目大意:
有n个学生将参与考试。对于学生i,如果他考得好的话可以考 a i a_i ai分,否则只能考 b i b_i bi分。假设这些学生中的第一名考了x分。那么考试分数不低于 x ⋅ p % x\cdot p\% xp%的学生都将通过考试。求所有情况下,能够通过考试的学生人数的最大值是多少。

思路:
尺取法:高效的枚举区间的方法,一般用于求取有一定限制的区间个数或最短的区间等。

尺取法通常适用于选取区间有一定规律,或者说所选取的区间有一定的变化趋势的情况,通俗地说,在对所选取区间进行判断之后,我们可以明确如何进一步有方向地推进区间端点以求解满足条件的区间,如果已经判断了目前所选取的区间,但却无法确定所要求解的区间如何进一步根据其端点得到,那么尺取法便是不可行的。首先,明确题目所需要求解的量之后,区间左右端点一般从整个数组的起点开始,之后判断区间是否符合条件,再根据实际情况变化区间的端点求解答案。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6+10;
struct stu{
    int id;
    ll sorce;
}st[maxn];
int vis[maxn];
bool cmp(stu a,stu b){
    return a.sorce<b.sorce;
}
int main(){
    int t;
    scanf("%d",&t);
    for(int tt=1;tt<=t;tt++){
        int n, p;
        scanf("%d%d",&n,&p);
        for(int i=1;i<=n;i++){
        	int x,y; 
            scanf("%d%d",&x,&y);
            st[i]={i,x};
            st[n+i]={i,y};
            vis[i]=0;//每次清空
        }
        n*=2; 
        int ans=1;//至少有一个人及格 
		int head=1,tail=0,now=0;//now记录当前人数
        sort(st+1,st+n+1,cmp);
        while(now!=n/2){//先找出n个人
        	tail++;//尾指针后移
            if(vis[st[tail].id]==0) now++;//第一次出现的学生
            vis[st[tail].id]++;//记录该学生的出现次数
        }
        //vis[st[tail].id]的值一定是1!
        //因为进入下面的循环时,now和vis[st[tail].id]会马上进行修正,所以为避免重复计算,这里要先把当前tail指针指向的id和人数减1
        vis[st[tail].id]--;
        now--;
        //原来这就是尺取!
        while(tail<=n){
            if(vis[st[tail].id]==0) now++;
			vis[st[tail].id]++;
            while(st[head].sorce*100<st[tail].sorce*p){//不及格的学生,就去掉,头指针前移,人数减1
                vis[st[head].id]--;
                if(vis[st[head].id]==0) now--;
                head++;//推进区间端点
            }
            ans=max(now,ans);//维护最大值
            tail++;//尾指针不断后移
        }
        printf("Case #%d: %d\n",tt,ans);
    }
    return 0;
}
/**
5 10
1 10 2 9 3 8 4 7 5 6
**/ 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值