2020-10-17

xju第一场训练赛

A.倍数

考点:容斥原理

给出一个数N,求1至N中,有多少个数不是2 3 5 7的倍数。 例如N = 10,只有1不是2 3 5 7的倍数。

Input

输入1个数N(1 <= N <= 10^18)。

Output

输出不是2 3 5 7的倍数的数共有多少。

Sample Input

10

Sample Output

1

我的思路:

if(n%2!=0||n%3!=0||n%5!=0||n%7!=0)

ans++;

正确思路:

分析: 之前做过几道容斥,没仔细总结过,顺便总结下趁着这个裸题 -
当两个数或三个的时候都好分析,这次是四个,也不难分析,如果多的话就不好分析了,先看四个的,我们形象的用集合的形式来表示2,3,5,7倍数
这里写图片描述

当一个数为n时,显然,所有2,3,5,7倍数的个数就是sum(甲,乙,丙,丁,Ⅰ,ⅡⅢ,Ⅳ,A,B,C,D,E),我们可以用n/2表示所有是2的倍数的个数也就相当于图中的sum(甲,Ⅰ,Ⅱ,A,B,C,E),当然n/3,n/5,n/7同理,我们发现如果把n/2,n/3,n/5,n/7加起来的话,答案是超的,其中红色为多算了一次,蓝色为多算了两次,紫色为多算了三次,我们现在想办法减掉。
我们可以这样减去一个n/(2*3),相当于sum(I,A,B,D,E),同理再减去n/(3*7)n/(7*5)n/(5*2)
我们如果都按照集合的方法结果正好缺一个E,如果我们再加上个E的话,算出来其实结果是不对的,算多了,为什么呢,细心地话就可以发现,其实不止这一种组合方式,也可以如下
这里写图片描述
所以我们会多算,那应该怎么算呢,才能保证正确的容斥呢,我们可以把每个交集都减去,这样就可避免了,也就是说在原来的基础上再减去 n/(2*7),n/(3*5),这两项,我们发现加了之后就会产生新的问题,答案缺少了A,B,C,D,我们发现n/(2*3*5) 代表sum(A,E),同理n/(2*3*7),n/(7*3*5),n/(2*7*5),我们可以发现如果加上之后正好多了个E,减去即可,而E = n/(2*3*5*7),从而答案得到了解决,代码很简单,主要是思想。
写出来后可以发现容斥的通式为

这里写图片描述

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;

int main() {
    ll n;cin>>n;
    ll sum = n/2 + n/3 + n/5 + n/7 - n/6 - n/10 - n/35 - n/21 - n/14 - n/15 + n/30 + n/42 + n/70 + n/105 - n/210;
    cout<<n-sum<<endl;
}

转自:

https://wangzhe.blog.csdn.net/article/details/78604131?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-5.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-5.nonecase(侵删)

 

同类型的题目:

P3197 [HNOI2008]越狱

题目描述

监狱有 nn 个房间,每个房间关押一个犯人,有 mm 种宗教,每个犯人会信仰其中一种。如果相邻房间的犯人的宗教相同,就可能发生越狱,求有多少种状态可能发生越狱。

答案对 100,003100,003 取模。

输入格式

输入只有一行两个整数,分别代表宗教数 mm 和房间数 nn。

输出格式

输出一行一个整数代表答案。

输入输出样例

输入 #1复制

2 3

输出 #1复制

6

说明/提示

样例输入输出 1 解释

状态编号1 号房间2 号房间3 号房间
1信仰 1信仰 1信仰 1
2信仰 1信仰 1信仰 2
3信仰 1信仰 2信仰 2
4信仰 2信仰 1信仰 1
5信仰 2信仰 2信仰 2
6信仰 2信仰 2信仰 1

数据规模与约定

对于 100\%100% 的数据,保证 1 \le m \le 10^81≤m≤108,1 \le n \le 10^{12}1≤n≤1012。

正确解

【题解】

100100分满分思路:

由于直接计算越狱的方案数不是很好做,所以我们选择用所有的方案数减去不越狱的方案数。由于每一个房间的人的宗教有mm种可能,且有nn个房间,所以所有的方案数等于m^nmn。由于当相邻的两个房间所关押的罪犯当宗教相同时就有可能会越狱,所以不可能会越狱的方案数为m \times (m-1)^{n-1}m×(m−1)n−1,这是因为第一个房间的最烦的宗教有mm种选择,而第二个房间的罪犯的宗教有m-1m−1种选择,第三个房间的罪犯的宗教有m-2m−2种选择,以此类推。因此越狱的方案数就有m^n-m \times (m-1)^{n-1}mn−m×(m−1)n−1种。由于nn很大,所以我们用快速幂来解这道题,注意,本题的模数是100003,不要输错了。

什么是快速幂?

快速幂可以快速地求出一个数的多少次方是多少(可能会对某个数取模)。

如果我们求的是a^nan的值的话,那么有性质:

a^b\;mod\;c=((a^2)^{\lfloor b/2 \rfloor} \times a)\;mod\;cabmodc=((a2)⌊b/2⌋×a)modc,bb是奇数。

a^b\;mod\;c=((a^2)^{\lfloor b/2 \rfloor})\;mod\;cabmodc=((a2)⌊b/2⌋)modc,bb是偶数。

由于在加法和乘法运算中对于取模运算时自由的(即中途取模和结束时再取模的答案是一样的),所以我们可以在中途取模,下面是一些取模运算的规则,如下:

模运算与基本四则运算有些相似,但是除法例外。其规则如下:

(a + b) % p = (a % p + b % p) % p

(a – b) % p = (a % p – b % p) % p

(a * b) % p = (a % p * b % p) % p

ab % p = ((a % p)b) % p

结合率:

((a+b) % p + c) % p = (a + (b+c) % p) % p

((ab) % p * c)% p = (a * (bc) % p) % p

 

#include <cstdio>
long long p=0;
long long qu(long long x,long long y)
{
    if(y==0)
    {
        return 1;
    }
    else
    {
        long long dq=qu(x,y/2);
        if(y%2==0)
        {
            return ((dq%p)*(dq%p))%p;
        }
        else if(y%2==1)
        {
            return ((dq%p)*(dq%p)*(x%p))%p;
        }
    }
}
int main()
{
    long long m=0,n=0;
    scanf("%lld %lld",&m,&n);
    p=100003;
    printf("%lld",(qu(m,n)-(m*qu(m-1,n-1))%p+p)%p);
    return 0;
}

转自 https://www.luogu.com.cn/problem/solution/P3197侵删

B - 质中质

考点:质数筛

如果一个质数,在质数列表中的编号也是质数,那么就称之为质数中的质数。例如:3 5分别是排第2和第3的质数,所以他们是质数中的质数。现在给出一个数N,求>=N的最小的质数中的质数是多少(可以考虑用质数筛法来做)。

Input

输入一个数N(N <= 10^6)

Output

输出>=N的最小的质数中的质数。

Sample Input

20

Sample Output

31

我的思路:

思路来自桶排法,先把所有质数标记,然后再选取第一个比大的数

数据太大,时间太长

正确解法:

#include<bits/stdc++.h> 
using namespace std;
const int N=10000010,NN=10000000;
int n,p[N],num=0;
bool np[N];

int main()
{
    int i,j;
    scanf("%d",&n);
    np[0]=np[1]=1;//0和1都不为素数                                                                                    
    for(i=2;i<=NN;i++)
    {
        if(!np[i]) p[++num]=i;//if(np[i]==0)//若当前数i没有被之前的所有数筛掉,表明i是素数,将i添加进素数表prime
        for(j=1;j<=num&&p[j]*i<=NN;j++)//界在10e6 
        {
            np[p[j]*i]=1;//进行筛选,所有之前出现的素数的倍数都会被标记 
            if(i%p[j]==0) break;/*如果i%prime[j]==0,那么i就可以看成prime[i]乘以一个数(记为m);
			因为我们存的prime是从小到大存的,所以prime[j+1]>prime[j];那么i * prime[j+1]就可以看
			成prime[j] * m * prime[j+1];那么i * prime[j+1]的最小质因子是prime[j]。加上
			if(i%prime[j]==0) break后我们就可以保证每一个数只被它的最小质因子给筛去,即每一个数
			只会被筛一次。
		举个例子,6=2*3;当i=2时i%2=0,跳出循环,那它就不会被质因子3给筛去;当i=3时,6就被质因子2给筛去。*/
        }
    }
    for(i=1;i<=num;i++)
        if(p[i]>=n&&(!np[i]))
        {
            printf("%d",p[i]);
            break;
        }
    return 0;
}

这种方法叫做欧拉筛(也叫质数线性筛)

比朴素法以及埃氏法好在不重复,取得是最小质因子

 

同类型的题目:

P3383 【模板】线性筛素数

题目背景

本题已更新,从判断素数改为了查询第 kk 小的素数
提示:如果你使用 cin 来读入,建议使用 std::ios::sync_with_stdio(0) 来加速。

题目描述

如题,给定一个范围 nn,有 qq 个询问,每次输出第 kk 小的素数。

输入格式

第一行包含两个正整数 n,qn,q,分别表示查询的范围和查询的个数。

接下来 qq 行每行一个正整数 kk,表示查询第 kk 小的素数。

输出格式

输出 qq 行,每行一个正整数表示答案。

输入输出样例

输入 #1复制

100 5
1
2
3
4
5

输出 #1复制

2
3
5
7
11

说明/提示

【数据范围】
对于 100\%100% 的数据,n = 10^8n=108,1 \le q \le 10^61≤q≤106,保证查询的素数不大于 nn。

正确答案:

#include<bits/stdc++.h>
bool isPrime[100000010];
int Prime[6000010], cnt = 0;
void GetPrime(int n)
{
	memset(isPrime, 1, sizeof(isPrime));
	isPrime[1] = 0;
	
	for(int i = 2; i <= n; i++)
	{
		if(isPrime[i])
			Prime[++cnt] = i;
			
		for(int j = 1; j <= cnt && i*Prime[j] <= n; j++) 
		{
        
			isPrime[i*Prime[j]] = 0;
            
			if(i % Prime[j] == 0)
				break; 
		}
	}
}

int main()
{
	int n, q;
	scanf("%d %d", &n, &q);
	GetPrime(n);
	while (q--)
	{
		int k;
		scanf("%d", &k);
		printf("%d\n", Prime[k]);
	}
	return 0;
}

(xixi顺便扒了个板子,爆零的蒟蒻重头学起) 

C - 游戏1

考点:博弈论-巴什博弈

有一堆石子共有N个。A B两个人轮流拿,A先拿。每次最少拿1颗,最多拿K颗,拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出N和K,问最后谁能赢得比赛。

例如N = 3,K = 2。无论A如何拿,B都可以拿到最后1颗石子。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数N,K。中间用空格分隔。(1 <= N,K <= 10^9)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

4
3 2
4 2
7 3
8 3

Sample Output

B
A
A
B

我的理解:

根据样例先减去最大值,前面一人一个拿,如果为奇数则后手以后赢

正确解法

#include<bits/stdc++.h>
using namespace std;
int n,k,t;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&k);
        if (n<=k) printf("A\n");//如果可以一次拿完A必胜 
        else
        {
            n%=(k+1);
            if (n==0)
                printf("B\n");
            else
                printf("A\n");
        }
    }
    return 0;
}

 

你以为我会一道道来? 我突然发现四种常见博弈

我们先来看这道题的兄弟

G - 游戏2

 

有2堆石子。A B两个人轮流拿,A先拿。每次可以从一堆中取任意个或从2堆中取相同数量的石子,但不可不取。拿到最后1颗石子的人获胜。假设A B都非常聪明,拿石子的过程中不会出现失误。给出2堆石子的数量,问最后谁能赢得比赛。

例如:2堆石子分别为3颗和5颗。那么不论A怎样拿,B都有对应的方法拿到最后1颗。

Input

第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 10000) 第2 - T + 1行:每行2个数分别是2堆石子的数量,中间用空格分隔。(1 <= N <= 2000000)

Output

共T行,如果A获胜输出A,如果B获胜输出B。

Sample Input

3
3 5
3 4
1 9

Sample Output

B
A
A

然后看我找到了什么

https://blog.csdn.net/MM__1997/article/details/72903794?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160301059019724839220251%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=160301059019724839220251&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_v2~rank_v28-1-72903794.first_rank_ecpm_v3_pc_rank_v2&utm_term=%E5%B7%B4%E4%BB%80%E5%8D%9A%E5%BC%88&spm=1018.2118.3001.4187(侵删)

D - 环

考点:约瑟夫环

N个人坐成一个圆环(编号为1 - N),从第1个人开始报数,数到K的人出列,后面的人重新从1开始报数。问最后剩下的人的编号。

例如:N = 3,K = 2。2号先出列,然后是1号,最后剩下的是3号。

Input

2个数N和K,表示N个人,数到K出列。(2 <= N, K <= 10^6)

Output

最后剩下的人的编号

Sample Input

3 2

Sample Output

3

正确解法:

#include<bits/stdc++.h>
using namespace std;
int n,k,ans,i;
int main()
{
    scanf("%d%d",&n,&k);
    for(i=2;i<=n;i++) ans=(ans+k)%i;
    printf("%d",ans+1);
    return 0;
}

wtf???

这是啥

经国我询问以及查询

发现约瑟夫环还可以用模拟做

https://blog.csdn.net/weixin_42659809/article/details/82596676?biz_id=102&utm_term=%E7%BA%A6%E7%91%9F%E5%A4%AB%E7%8E%AF&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-82596676&spm=1018.2118.3001.4187

事实证明大佬刚开始也是十分无解的 

同类型(温柔版(难版)):

P2696 慈善的约瑟夫

题目描述

你一定听说过约瑟夫问题吧?即从N个人中找出唯一的幸存者。现在老约瑟夫将组织一个皆大欢喜的新游戏,假设N个人站成一圈,从第1人开始交替的去掉游戏者,但只是暂时去掉,直到最后剩下唯一的幸存者为止。幸存者选出后,所有比幸存者号码高的人每人得到1个金币,永久性离开。其余剩下的将重复以上的游戏过程,比幸存者号码主的人每人得到1个金币后离开。经过这们的过程后,一旦人数不再减少,则最后剩下的那些人将得到2个金币。请你计算一下老约瑟夫一共要付出多少钱?

输入格式

一行一个正整数N表示人数。

输出格式

一行一个正整数表示共需支付的钱数。

输入输出样例

输入 #1复制

10

输出 #1复制

13

说明/提示

1<=N<=100000

题解:

不会!会再回来更新

P1424 小鱼的航程(改进版)

题目描述

有一只小鱼,它平日每天游泳 250 公里,周末休息(实行双休日),假设从周 x(1\le x \le 7)x(1≤x≤7) 开始算起,过了 n(n\le 10^6)n(n≤106) 天以后,小鱼一共累计游泳了多少公里呢?

输入格式

输入两个整数x,n(表示从周x算起,经过n天)。

输出格式

输出一个整数,表示小鱼累计游泳了多少公里。

输入输出样例

输入 #1复制

3 10

输出 #1复制

2000

这道题虽然水,但是也涉及到了循环

可以为后来不需要删去数据的做个例子

AC代码

#include<bits/stdc++.h>
using namespace std;
int main()
{
 	unsigned long long n,ans=0; 
    int x;
    cin >> x >> n; 
	for(int i=0;i<n;i++)
	{
		if((x!=6)&&(x!=7))
		ans+=250;
		if(x==7)
		x=1;
		else
		x++;
	}
	cout<<ans;
	return 0;
 } 

E - 覆盖

考点:经典dp

在2*N的一个长方形方格中,用一个1*2的骨牌排满方格。

问有多少种不同的排列方法。

例如:2 * 3的方格,共有3种不同的排法。(由于方案的数量巨大,只输出 Mod 10^9 + 7 的结果)

 

Input

输入N(N <= 1000)

Output

输出数量 Mod 10^9 + 7

Sample Input

3

Sample Output

3

正确的解法:

#include<bits/stdc++.h>
using namespace std;
const int mo=1000000007;
int n,i;
long long f[1010];
int main()
{
    scanf("%d",&n);
    f[0]=f[1]=1;
    f[2]=2;
    for(i=3;i<=n;i++) f[i]=(f[i-1]+f[i-2])%mo;
    printf("%lld",f[n]);
    return 0;
}

 

 

F - 3的幂的和

 

求:3^0 + 3^1 +...+ 3^(N) mod 1000000007

Input

输入一个数N(0 <= N <= 10^9)

Output

输出:计算结果

Sample Input

3

Sample Output

40

正确的答案:

#include<bits/stdc++.h>
using namespace std;
const long long mo=1000000007;
long long n;
long long ksm(long long x,long long y)
{
    long long ret=1;
    while(y)
    {
        if(y&1) ret=(ret*x)%mo;
        x=(x*x)%mo;
        y>>=1;
    }
    return ret;
}
int main()
{
    scanf("%lld",&n);
    printf("%lld",(ksm(3,n+1)-1)*ksm(2,mo-2)%mo);
    return 0;
}

H - 线段的重叠

 

X轴上有N条线段,每条线段包括1个起点和终点。线段的重叠是这样来算的,    1020和    1225的重叠部分为    1220。

给出N条线段的起点和终点,从中选出2条线段,这两条线段的重叠部分是最长的。输出这个最长的距离。如果没有重叠,输出0。

Input

第1行:线段的数量N(2 <= N <= 50000)。 第2 - N + 1行:每行2个数,线段的起点和终点。(0 <= s , e <= 10^9)

Output

输出最长重复区间的长度。

Sample Input

5
1 5
2 4
2 8
3 7
7 9

Sample Output

4

正确的答案:

#include<bits/stdc++.h>
using namespace std;
const int N=50010;
int n,ans,i;
struct node
{
    int l,r;
}a[N];
bool cmp(node x,node y)
{
    if(x.l==y.l) return x.r>y.r;
    return x.l<y.l;
}
int main()
{
    scanf("%d",&n);
    for(i=1;i<=n;i++) scanf("%d%d",&a[i].l,&a[i].r);
    sort(a+1,a+n+1,cmp);
    for(i=2;i<=n;i++)
    {
        if(a[i].r>=a[i-1].r) ans=max(ans,a[i-1].r-a[i].l);
        else
        {
            ans=max(ans,a[i].r-a[i].l);
            a[i]=a[i-1];
        }
    }
    printf("%d",ans);
    return 0;
}

I - 蚂蚁

n只蚂蚁以每秒1cm的速度在长为Lcm的竿子上爬行。当蚂蚁爬到竿子的端点时就会掉落。由于竿子太细,两只蚂蚁相遇时,它们不能交错通过,只能各自反向爬回去。对于每只蚂蚁,我们知道它距离竿子左端的距离xi,但不知道它当前的朝向。请计算各种情况当中,所有蚂蚁落下竿子所需的最短时间和最长时间。

例如:竿子长10cm,3只蚂蚁位置为2 6 7,最短需要4秒(左、右、右),最长需要8秒(右、右、右)。

Input

第1行:2个整数N和L,N为蚂蚁的数量,L为杆子的长度(1 <= L <= 10^9, 1 <= N <= 50000) 第2 - N + 1行:每行一个整数Aii,表示蚂蚁的位置(0 < Aii < L)

Output

输出2个数,中间用空格分隔,分别表示最短时间和最长时间。

Sample Input

3 10
2
6
7

Sample Output

4 8

我的困惑:

无法确定最短时间的中间那一只的位置

正确的答案:

#include<bits/stdc++.h> 
using namespace std;
int n,l,mxans,mians,i,a;
int main()
{
    scanf("%d%d",&n,&l);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&a);
        mians=max(mians,min(a,l-a));
        mxans=max(mxans,max(a,l-a));
    }
    printf("%d %d",mians,mxans);
    return 0;
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值