寒假总结(习题+感悟)

牛客寒假六日游

(一)2020牛客寒假算法基础集训营1

A - honoka和格点三角形

题目描述

链接:https://ac.nowcoder.com/acm/contest/3002/A
来源:牛客网
honoka最近在研究三角形计数问题。
她认为,满足以下三个条件的三角形是“好三角形”。
1.三角形的三个顶点均为格点,即横坐标和纵坐标均为整数。
2.三角形的面积为 1。
3.三角形至少有一条边和 x 轴或 y 轴平行。
honoka想知道,在平面中选取一个大小为 n*m 的矩形格点阵,可以找到多少个不同的“好三角形”?由于答案可能过大,请对 1000000007 取模。

Input

两个正整数 n 和 m (2 ≤ n,m ≤ 10^9)

Output

面积为1的格点三角形的数量,对10^9 + 7取模的结果。

Sample Input

样例1:
2 3
样例2:
100 100

Sample Output

样例1:
6
样例2:
7683984

说明

格点如下:
& & &
& & &
不妨设左下角坐标为(1,1),右上角坐标为到(3,2)。
那么三点坐标可选:
(1,1)(1,2)(3,1)
(1,1)(1,2)(3,2)
(1,1)(2,2)(3,1)
(1,1)(3,1)(3,2)
(1,2)(2,1)(3,2)
(1,2)(3,1)(3,2)
所以共有6个。

理解

首先面积为1的三角形有两种(底为1高为2+底为2高为1)
最外面的两行点只能朝里行构成三角形,而中间的点行可以向两边都构成三角形(划重点)
一行上长度为2的底分别有n-2和m-2个,此时高为1,临行有多少点就有多少个三角形(中间行记得翻倍)
一行上长度为1的底分别有n-1和m-1个,此时高为2,临隔行有点就有多少三角形

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 3e5 + 7;
const int mod = 1e9 + 7;

int main(){
	ll n,m;
	scanf("%lld %lld",&n,&m);
	if(n == 2 && m == 2) printf("0\n");
	ll sum = 0;
	sum += ((m - 2ll)*((n-1ll)%mod*2ll%mod*m%mod)%mod + (n-2ll)*((m-1ll)%mod*2ll%mod*n%mod)%mod)%mod;
	sum += ((((m - 2)*(n - 1)%mod)*(n-2)%mod)*2 % mod + (((m - 1)*(n - 2)%mod)*(m-2)%mod)*2%mod)%mod;
	sum %= mod;
	printf("%lld\n",(sum+mod)%mod);
	return 0; 
}

B - rin和快速迭代

题目描述

链接:https://ac.nowcoder.com/acm/contest/3002/E
来源:牛客网
”数论真的太好玩了喵~“——hoshizora rin
rin最近喜欢上了数论。
然而数论实在太复杂了,她只能研究一些简单的问题。
这天,她在研究正整数因子个数的时候,想到了一个“快速迭代”算法。设 f(x) 为 x 的因子个数,将 f 迭代下去,rin猜想任意正整数最终都会变成 2 。
例如:f(12) = 6 , f(6) = 4 , f(4) = 3 ,f(3) = 2 。
她希望你帮她验证一下。她会给你一个正整数 n ,让你输出它在迭代过程中,第一次迭代成 2 的迭代次数。

Input

一个正整数 n ( 3 <= n <= 10^12 )

Output

一个正整数,为 n 迭代至 2 的次数。

Sample Input

12

Sample Output

4

说明

12的因子:1,2,3,4,6,12。共6个。
6的因子:1,2,3,6。共4个。
4的因子:1,2,4。共3个。
3的因子:1,3。共2个。
12 → 6 → 4 → 3 → 2 , 故迭代了4次。

理解

虽然n到10^12但是暴力枚举因子只要循环到10^6就可以了,所以直接暴力完事

AC代码

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;

ll get_num(ll n){
    int tot=1;
    for(ll i=2;i*i<=n;++i){
        if(n%i==0){
            int x=0;
            while(n%i==0){
                n/=i;
                x++;
            }
            tot*=(x+1);
        }
    }
    if(n>1)tot*=2;
    return tot;
}
int main(){
    ll n;
    ll cnt = 0;
    scanf("%lld",&n);
    while(n != 2){
    	cnt++;
        n = get_num(n);
    }
    printf("%lld\n",cnt);
    return 0;
}

C - nozomi和字符串

题目描述

链接:https://ac.nowcoder.com/acm/contest/3002/H
来源:牛客网
nozomi看到eli在字符串的“花园”里迷路了,决定也去研究字符串问题。
她想到了这样一个问题:
对于一个 “01” 串而言,每次操作可以把 0 字符改为 1 字符,或者把 1 字符改为 0 字符。所谓 “01” 串,即只含字符 0 和字符 1 的字符串。
nozomi有最多 k 次操作的机会。她想在操作之后找出一个尽可能长的连续子串,这个子串上的所有字符都相同。
nozomi想问问聪明的你,这个子串的长度最大值是多少?
注:k 次操作机会可以不全部用完。
如果想知道连续子串的说明,可以去问问eli,nozomi不想再讲一遍。

Input

第一行输入两个正整数 n 和 k ( 1 <= k <= n <= 200000 )
输入仅有一行,为一个长度为 n 的、仅由字符 0 和 1 组成的字符串。

Output

一个正整数,为满足条件的子串长度最大值。

Sample Input

5 1
10101

Sample Output

3

说明

只有 1 次操作机会。
将第二个位置的 0 改成 1 ,字符串变成 11101,可以选出 “111”子串,长度为 3 。
如果修改第三个或者第四个位置的字符也可以选出长度为 3 的子串。

理解

先用一个二维vector记录1和0出现的位置
如果k大于其中一个数组的大小说明所有的数学都能化成同一个
否则就看每隔k个不通0/1相隔的大小,因为你可以吧这k个变化成另一个,这样就能算出反向最长,然后max比较,保留最大

AC代码

/*
11 3
11000010101
*/
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn = 2e5 + 7;
vector<int> idx[2];

int main(){
    int n,k;
    scanf("%d %d",&n,&k);
    string s;
    cin >> s;
    for(int i = 0;i < s.size();i++){
    	idx[s[i]-'0'].push_back(i);
	}
	if(k >= idx[0].size() || k >= idx[1].size()){
		cout << n << endl;
		return 0;
	} 
	int ans = 0;
	for(int i = 0;i < 2;i++){
		for(int j = 0;j < idx[i].size()-k+1;j++){
			if(j == 0){
				ans = max(ans,idx[i][j+k]-1);
				continue;
			}
			if(j + k == idx[i].size()){
				ans = max(ans,n-1-idx[i][j-1]);
				continue; 
			}
			ans = max(ans,idx[i][j+k]-1-idx[i][j-1]);
		}
	}
	cout << ans << endl;
    return 0;
}

D - nico和niconiconi

题目描述

链接:https://ac.nowcoder.com/acm/contest/3002/I
来源:牛客网
“にっこにっこにー” ——nico
nico平时最喜欢说的口头禅是niconiconi~。
有一天nico在逛著名弹幕网站"niconico"的时候惊异的发现,n站上居然有很多她的鬼畜视频。其中有一个名为《让nico为你洗脑》的视频吸引了她的注意。
她点进去一看,就被洗脑了:“niconicoh0niconico*^vvniconicoG(vniconiconiconiconiconicoG(vniconico…”
弹幕中刚开始有很多“nico1 nico2”等计数菌,但到后面基本上都是“计数菌阵亡”的弹幕了。
nico也想当一回计数菌。她认为:“nico” 计 a 分,“niconi” 计 b 分,“niconiconi” 计 c 分。
她拿到了一个长度为 n 的字符串,请帮她算出最大计数分数。
注:已被计数过的字符不能重复计数!如"niconico"要么当作"nico"+“nico"计 2a 分,要么当作"niconi”+"co"计 b 分。

Input

第一行四个正整数 n,a,b,c。
( 1<=n <= 300000 , 1 <= a,b,c <= 10^9 )
第二行是一个长度为 n 的字符串。

Output

一个整数,代表最大的计数分数。

Sample Input

19 1 2 5
niconiconiconiconi~

Sample Output

7

说明

“niconi"co"niconiconi”~
故为2+5=7分

理解

起先用贪心wa了好多发,然后被代佬提醒改用dp

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 3e5 + 7;
ll dp[maxn];
string s;
int c1(int l,int r){
	if(l<1) return 0;
	string ss = s.substr(l,r-l+1);
	if(ss == "nico") return 1;
	return 0;
}
int c2(int l,int r){
	if(l<1) return 0;
	string ss = s.substr(l,r-l+1);
	if(ss == "niconi") return 1;
	return 0;
}
int c3(int l,int r){
	if(l<1) return 0;
	string ss = s.substr(l,r-l+1);
	if(ss == "niconiconi") return 1;
	return 0;
}
int main(){
	ll n,a,b,c;
	scanf("%lld %lld %lld %lld",&n,&a,&b,&c);
	cin >> s;
	s = " " + s;
	int cnt = 0;
	for(int i = 1;i <= n;i++){
		dp[i] = dp[i-1];
		if(s[i] == 'o'){
			if(c1(i-3,i)) dp[i] = max(dp[i],dp[i-4]+a);
		}
		else if(s[i] == 'i'){
			if(c2(i-5,i)) dp[i] = max(dp[i],dp[i-6]+b);
			if(c3(i-9,i)) dp[i] = max(dp[i],dp[i-10]+c);
		}
	}
	cout << dp[n] << endl; 
	return 0; 
}

(二)2020牛客寒假算法基础集训营2

A - 算概率

题目描述

链接:https://ac.nowcoder.com/acm/contest/3003/C
来源:牛客网

牛牛刚刚考完了期末,尽管 牛牛 做答了所有 \text{}nn 道题目,但他不知道有多少题是正确的。
不过,牛牛 知道第 \text{}ii 道题的正确率是pi​。
牛牛 想知道这 n 题里恰好有0,1,…,n 题正确的概率分别是多少,对 10^9+7 取模。
对 10^9+7 取模的含义是:对于一个 b != 0 的不可约分数 a / b ,存在 q 使得 b×qmod(10^9+7) = a,q 即为 a / b 对 10^9+7 取模的结果。

Input

第一行,一个正整数 n 。
第二行,n 个整数 p1 , p2 , … , pn,在模 10^9+7 意义下给出。
保证 1 ≤ n ≤ 2000

Output

输出一行 n+1 个用空格隔开的整数表示答案(对 10^9+7 取模)。

Sample Input

1
500000004

Sample Output

500000004 500000004

说明

有 1 道题,做对的概率是 1 / 2 ( 1 / 2 在模 10^9+7 意义下为 500000004 )。

理解

就是一个普通dp题,循环当前题号和做对的题数,层层递推

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e3 + 7;
const int mod = 1e9 + 7;
ll dp[maxn][maxn],a[maxn];
int main(){
    int n;
    scanf("%d",&n);
    dp[0][0] = 1;
    for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
    for(int i = 1;i <= n;i++){
        for(int j = 0;j <= i;j++){
            if(!j) dp[i][j] = (dp[i-1][j] * (1-a[i])) % mod;
            else dp[i][j] = (dp[i-1][j]*(1-a[i])%mod+dp[i-1][j-1]*a[i]%mod)%mod;
        }
    }
    cout << (dp[n][0]+mod) % mod;
    for(int i = 1;i <= n;i++){
        printf(" %lld",(dp[n][i]+mod)%mod);
    }
    return 0;
}

B - 拿物品

题目描述

链接:https://ac.nowcoder.com/acm/contest/3003/F
来源:牛客网
牛牛和 牛可乐 面前有 n 个物品,这些物品编号为 1,2,…,n ,每个物品有两个属性 ai, bi。
牛牛与 牛可乐会轮流从剩下物品中任意拿走一个, 牛牛先选取。
设 牛牛选取的物品编号集合为 H,牛可乐选取的物品编号的集合为 T,取完之后,牛牛 得分为∑ i∈H ai ;而 牛可乐得分为∑ i∈T​ bi。
牛牛和 牛可乐都希望自己的得分尽量比对方大(即最大化自己与对方得分的差)。
你需要求出两人都使用最优策略的情况下,最终分别会选择哪些物品,若有多种答案或输出顺序,输出任意一种。

Input

第一行,一个正整数 n,表示物品个数。
第二行,n 个整数 a1,a2,…,an ,表示 n 个物品的 A 属性。
第三行,n 个整数 b1,b2 ,…,bn,表示 n 个物品的 B 属性。
保证 2 ≤ n ≤ 2e5,0 ≤ ai,bi​ ≤ 10^9 。

Output

输出两行,分别表示在最优策略下 牛牛和 牛可乐各选择了哪些物品,输出物品编号。

Sample Input

3
8 7 6
5 4 2

Sample Output

1 3
2

说明

3 1
2
也会被判定为正确

理解

写了一年的排序,wa了一年
后来发现可以计算同一物品总值最大,再选对自己有利的
因为拿走了该物品,自己获得ai对方损失bi所以该算和最大

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
int vis[maxn];
struct node{
	int num,a;
}x[maxn];
struct Node{
	int num,b;
}y[maxn];
int cmp(node s,node ss){
	return s.a > ss.a;
}
int bmp(Node s,Node ss){
	return s.b > ss.b;
}
int main(){
	int n;
	scanf("%d",&n);
	memset(vis,0,sizeof(vis));
	for(int i = 1;i <= n;i++){
		scanf("%d",&x[i].a);
		x[i].num = i;
	}
	for(int i = 1;i <= n;i++){
		scanf("%d",&y[i].b);
		y[i].num = i;
	}
	sort(x+1,x+n+1,cmp);
	sort(y+1,y+n+1,bmp);
	int idx1 = 0,idx2 = 0;
	int num1[maxn],num2[maxn];
	int i = 1,j = 1;
	while(idx1 + idx2 < n){
		while(vis[x[i].num]){
			i++;
		}
		num1[idx1++] = x[i].num;
		vis[x[i].num] = 1;
		i++;
		if(idx1 + idx2 >= n) break;	
		while(vis[y[j].num]){
			j++;
		}
		num2[idx2++] = y[j].num;
		vis[y[j].num] = 1;
		j++;
	}
	int fl = 0;
	for(int i = 0;i < idx1;i++){
		if(!fl){
			fl = 1;
			printf("%d",num1[i]);
		}
		else printf(" %d",num1[i]);
	}
	printf("\n");
	fl = 0;
	for(int i = 0;i < idx2;i++){
		if(!fl){
			fl = 1;
			printf("%d",num2[i]);
		}
		else printf(" %d",num2[i]);
	}
	printf("\n");
	return 0; 
}

C - 判正误

题目描述

链接:https://ac.nowcoder.com/acm/contest/3003/G
来源:牛客网
2020-2-17 数据相较赛时有加强,但未重测此前提交。
牛可乐有七个整数 a,b,c,d,e,f,g 并且他猜想 a^d +b^e + c^f = g。但 牛可乐无法进行如此庞大的计算。
请验证 牛可乐的猜想是否成立。

Input

第一行一个正整数 T,表示有 T 组数据。
每组数据输入一行七个整数 a,b,c,d,e,f,g 。
保证 1 ≤ T ≤ 1000 , -10^9 ≤ a,b,c,g ≤ 10^9 , 0≤d,e,f≤10^9
保证不会出现指数和底数同为 0 的情况。

Output

每组数据输出一行,若猜想成立,输出 Yes ,否则输出 No。

Sample Input

2
1 1 4 5 1 4 258
114514 1919810 1 2 3 4 1

Sample Output

Yes
No

说明

链接:https://ac.nowcoder.com/acm/contest/3003/G
来源:牛客网

1^5 + 1^1 + 4^4 = 258
114514^2 + 1919810^3 + 1^4 !=1

理解

当时觉得会t一年,就先没写
然后各位代佬发现快速幂偷鸡可以过,然后写了一手快速幂取模wa了
然后发现还得偷鸡模数改成1e9+9(然后神奇AC
到今天做总结时发现她加了数据
原来1e9+9 的模数也会wa 50%的数据了
然后我又用1e9+11偷掉了,好神奇的锻炼我偷鸡能力的题目嗷

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 11;
 
ll qp(ll a,ll b)
{
    ll result = 1;
    a = a % mod;
    while (b > 0){
        if (b % 2 == 1){
            result = result * a;
            result = result % mod;
            b--;
        }
        b /= 2;
        a = (a * a) % mod;
    }
    return result;
}
int main(){
    int cas;
    scanf("%d",&cas);
    while(cas--){
        ll a,b,c,d,e,f,g;
        scanf("%lld %lld %lld %lld %lld %lld %lld",&a,&b,&c,&d,&e,&f,&g);
        ll x = qp(a,d); ll y = qp(b,e); ll z = qp(c,f);
        if((x + y + z) % mod == g % mod) printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

D - 建通道

题目描述

链接:https://ac.nowcoder.com/acm/contest/3003/I
来源:牛客网
在无垠的宇宙中,有 n 个星球,第 i 个星球有权值 vi。
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是 lowbit(vi⊕vj),其中 ⊕ 为二进制异或,而 lowbit(x) 为 x 二进制最低位 1 对应的值,例如 owbit(5)=1,lowbit(8)=8。特殊地,lowbit(0)=0。
牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。

Input

第一行,一个正整数 n 。
第二行,n 个非负整数 v1,v2,…,vn。
保证 1≤n≤2×10^5, 0 ≤ vi ​< 2^30。

Output

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

Sample Input

2
1 2

Sample Output

1

说明

1、2 号点之间建立通道,v1 ⊕ v2 = 3,lowbit(3)=1

理解

用set去重,确定共有多少个点
然后因为以异或的方式算贡献,所以直接算每个位置上是否出现过1或0
因为&运算0&0=0;0&1=0;1&0=0;1&1=1

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
ll a[maxn];
set<ll> s;
int main(){
    int n;
    scanf("%d",&n);
    for(int i = 1;i <= n;i++){
    	scanf("%lld",&a[i]);
    	s.insert(a[i]);
	}
    ll idx = 1;
    set<ll>::iterator it;
    for(it =s.begin ();it!=s.end ();it++)
	    a[idx++] = *it;
	idx--;
    ll tp = (1<<30)-1,dow = 0;
    for(ll i = 1;i <= idx;i++){
    	tp &= a[i];
    	dow |= a[i]; 
	}
	ll sum = 0;
	for(ll i = 0;i < 30;i++){
		ll tmp = (1 << i);
		if(!(tp&tmp) && (dow&tmp)){
			sum = (1 << i);
			break;
		}
	}
    cout << sum * (idx-1) << endl;
    return 0;
}

(三)2020牛客寒假算法基础集训营3

A - 牛牛的汉诺塔

题目描述

链接:https://ac.nowcoder.com/acm/contest/3004/I
来源:牛客网
汉诺塔是一个经典问题,相传在古印度圣庙中,有一种被称为汉诺塔(Hanoi)的游戏。该游戏是在一块铜板装置上,有三根杆(编号A、B、C),在A杆自下而上、由大到小按顺序放置n个金盘。游戏的目标:把A杆上的金盘全部移到C杆上,并仍保持原有顺序叠好。操作规则:每次只能移动一个盘子,并且在移动过程中三根杆上都始终保持大盘在下,小盘在上,操作过程中盘子可以置于A、B、C任一杆上。
汉诺塔以及其衍生问题往往使用递归来求解,也是学习和理解递归很好的老师。
其伪代码如下

Function Hanoi(n,a,b,c)
    if n==1 then
        print(a+'->'+c)
    else
        Hanoi(n-1,a,c,b)
        print(a+'->'+c)
        Hanoi(n-1,b,a,c)
    end if
end Function 

牛牛很快就理解了代码的意思并且写出了求解汉诺塔的程序,他现在想研究汉诺塔的规律。
请你统计以下信息:A->B,A->C,B->A,B->C,C->A,C->B的次数,以及所有移动的总步数。

Input

仅一行,输入一个正整数n ( 1 ≤ n ≤ 60 )表示汉诺塔的层数。

Output

首先输出6行
A->B:XX
A->C:XX
B->A:XX
B->C:XX
C->A:XX
C->B:XX
分别表示每种移动情况出现的次数
最后输出一行
SUM:XX
表示所有移动情况的总和。

Sample Input

3

Sample Output

A->B:1
A->C:3
B->A:1
B->C:1
C->A:0
C->B:1
SUM:7

说明

伪代码所示算法的移动序列如下:
A->C
A->B
C->B
A->C
B->A
B->C
A->C
统计:
A->B出现1次
A->C出现3次
B->C出现1次
B->A出现1次
C->B出现1次
总计7次

理解

找规律题
打表显示前几组的数据,然后找规律就好啦

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
ll mp[70][10];
int main(){
    int n;
    scanf("%d",&n);
    mp[1][1] = mp[1][3] = mp[1][4] = mp[1][5] = mp[1][6] = 0;
    mp[1][2] = 1;
    mp[2][1] = 1; mp[2][2] = 1; mp[2][3] = 0; mp[2][4] = 1;
    mp[2][5] = 0; mp[2][6] = 0;
    for(int i = 3;i <= 60;i++){
        if(mp[i-1][1]!=mp[i-2][1]) mp[i][1] = mp[i-1][1];
        else mp[i][1] = mp[i-1][2]+mp[i-1][3];
         
        if(mp[i-1][2]!=mp[i-2][2]) mp[i][2] = mp[i-1][2];
        else mp[i][2] =mp[i-1][1]+mp[i-1][2]+mp[i-1][3]+1;
         
        if(mp[i-1][3]!=mp[i-2][3]) mp[i][3] = mp[i-1][3];
        else mp[i][3] = mp[i-1][4] + mp[i-1][5];
         
        mp[i][4] = mp[i][1];
         
        if(mp[i-1][5]!=mp[i-2][5]) mp[i][5] = mp[i-1][5];
        else mp[i][5] = mp[i][1] - i / 2;
          
        mp[i][6] = mp[i][3];
    }
    string s[7]={"d","A->B:","A->C:","B->A:","B->C:","C->A:","C->B:"};
    ll ans = 0;
    for(int i = 1; i <= 6;i++){
        cout << s[i] << mp[n][i] << endl;
        ans += mp[n][i];
    }
    cout << "SUM:"<<ans<< endl;
    return 0;
}

B - 牛牛的k合因子数

题目描述

链接:https://ac.nowcoder.com/acm/contest/3004/H
来源:牛客网
合数是指自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。
牛牛最近在研究“k合因子数”,所谓“k合数”是指一个数的所有因子中,是合数的因子共有k个。
例如20的因子有1,2,4,5,10,20,其中4,10,20为合数,它有3个合数因子,就称20是一个 “3合因子数”
牛牛想要知道1~n中给定k的情况下k合因子数的数目。

Input

第一行输入两个数字n,m (1 ≤ n,m ≤ 10^5 )表示范围以及查询“k”的数目
接下来m行,每行一个正整数k ( 1 ≤ k ≤ n )查询k合因子数的数目。

Output

一行一个数字,表示k合因子数的数目

Sample Input

10 5
1
2
3
4
5

Sample Output

4
1
0
0
0

说明

1~10的范围内
1合因子数有:4,6,9,10,共4个
2合因子数有:8,共1一个

理解

打个素数表然后暴力就好啦

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
int prime[maxn];
bool check[maxn];
int sum[maxn];
int idx = 0;
int pri(){
	memset(check, 0, sizeof(check));
	idx = 0;
    for (int i = 2; i <= maxn; i++){
        if (!check[i])
            prime[idx++] = i;	
        for (int j = 0; j < idx; j++){
            if (i*prime[j] > maxn)
                break;
            check[i*prime[j]] = 1;
            if ((i%prime[j]) == 0 )
                break; 
        }
    }
}
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	pri();
	for(int i = 1;i <= n;i++){
		int up = sqrt(i),cnt = 0;
		if(check[i]) cnt++;
		for(int j = 2;j <= up;j++){
			if(i % j == 0){
				if(j == up && i/j==j){
					if(check[j]) cnt += 1;
				}
				else{
					if(check[j]) cnt++;
					if(check[i / j]) cnt++;
				}
			}
		}
		sum[cnt]++;
	}
//	for(int i = 1;i <= n;i++){
//		printf("%d ",sum[i]);
//	}
//	cout << endl;
	while(m--){	
		int x;
		scanf("%d",&x);
		printf("%d\n",sum[x]);
	}
	return 0; 
}

(四)2020牛客寒假算法基础集训营4

A - 子段乘积

题目描述

链接:https://ac.nowcoder.com/acm/contest/3005/C
来源:牛客网
给出一个长度为 n 的数列 a1,a2,…,an,求其长度为 k 的连续子段的乘积对 998244353 取模余数的最大值。

Input

第一行两个整数n,k。
第二行n个整数,a1,a2,…,an。

Output

输出一个整数,代表最大余数。

Sample Input

5 3
1 2 3 0 8

Sample Output

6

说明

1 ∗ 2 ∗ 3 mod 998244353 = 6
备注
1 ≤ k ≤ n ≤ 2∗10^5
0 ≤ ai <998244353

理解

因为取模运算后再除数会出错
所以就用到了乘法逆元
不过我起先用扩展欧几里得求逆元wa了
后来改了费马小引理求解逆元就ac了,原因未知

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
const int mod = 998244353;
ll a[maxn];

ll exgcd(ll a,ll b,ll &x,ll &y){
    if(!b){
        x=1;
        y=0;
        return a;
    }
    ll d=exgcd(b,a%b,x,y);
    ll tmp=x;
    x=y;
    y=tmp-a/b*y;
    return d;
}
ll inv(ll a,ll m){
    ll x,y;
    ll d=exgcd(a,m,x,y);
    if(d==1){
        //处理负数
        return (x%m+m)%m;
    }
    return -1;
}

int main(){
	int n,k;
	scanf("%d %d",&n,&k);
	for(int i = 1;i <= n;i++) scanf("%lld",&a[i]);
	ll ans = 1;
	ll maxn = 0;
	int cnt = 0;
	for(int i = 1;i <= n;i++){
		if(a[i]){
			ans *= a[i];
			ans %= mod;
			cnt++;
		}else{
			cnt = 0;
			ans = 1;
		}
		if(cnt == k){
//			cout << "ans = " << ans << endl;
			maxn = max(maxn,ans);
			ans *= inv(a[i-k+1],mod);
			ans %= mod;
//			cout << "ans = " << ans << endl;
			cnt--;
		}
	}
	printf("%lld\n",maxn);
	return 0; 
}

B - 子段异或

题目描述

链接:https://ac.nowcoder.com/acm/contest/3005/D
来源:牛客网
输入一个数列a,你需要输出其中异或值为0的不同子段的数量。一个子段 [l,r] (1 ≤ l ≤ r ≤ n )的异或值为 al ⊕ al+1 ⊕ al+2 ⊕ … ⊕ ar,其中 ⊕ 符号代表异或运算。
两个子段被视为相同的,当且仅当其开始和结束位置均对应相同。

Input

第一行一个整数 n ,代表数列长度。
第二行 n 个整数,代表数列。

Output

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

Sample Input

5
1 2 3 2 1

Sample Output

2

说明

子段 [1,3] 和子段 [3,5] 是合法子段。
n ≤ 200000,0 ≤ ai​ ≤2^30−1

理解

计算前缀异或和,然后按出现次数算贡献就好了

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 7;
const int mod = 998244353;
map<ll,ll> mp;
int main(){
    int n;
    scanf("%d",&n);
    ll sum = 0;
    for(int i = 1;i <= n;i++){
        ll t;
        scanf("%lld",&t);
        sum = sum^t;
        mp[sum]++;
    }
    ll ans = mp[0];
    map<ll,ll>::iterator it;
    for(it = mp.begin();it != mp.end();it++){
        if(it -> second > 1){
            ll x = it ->second;
            ans += (x-1)*x/2;
        }
    }
    printf("%lld\n",ans);
    return 0;
}

C - 树上博弈

题目描述

链接:https://ac.nowcoder.com/acm/contest/3005/F
来源:牛客网
现有一个 n 个点,n-1条边组成的树,其中 1 号点为根节点。
牛牛和牛妹在树上玩游戏,他们在游戏开始时分别在树上两个不同的节点上。
在游戏的每一轮,牛牛先走一步,而后牛妹走一步。他们只能走到没有人的空节点上。如果谁移动不了,就输掉了游戏。现在牛牛和牛妹决定随机选择他们分别的起点,于是他们想知道,有多少种游戏开始的方式,使得牛牛存在一种一定获胜的最优策略。
两种开始方式相同,当且仅当在两种开始方式中牛牛,牛妹的开始位置是分别相同的,否则开始方式就被视作不同的。

Input

第一行输入为一个整数 n,代表树的点数。
第二行n-1个整数p2,p3,…,pn,分别代表2,3,…,n号点的父节点编号。

Output

一行一个整数,代表答案。

Sample Input

样例1:
3
1 2
样例2:
2
1
样例3:
30
1 1 2 1 2 1 3 2 3 4 2 3 1 2 3 4 2 4 5 6 3 4 12 12 12 13 13 13 13

Sample Output

样例1:
2
样例2:
0
样例3:
428

说明

样例1:
当且仅当牛牛在1号点,牛妹在3号点,或者牛牛在3号点,牛妹在1号点时,牛牛才获胜。
样例2:
由于无论如何牛牛都无路可走,因此必然牛妹获胜。
样例3:
QwQ
备注:
n ≤ 10^6
1 ≤ pi <i

理解

其实这就是一个染色问题,当两个点距离奇数步就是牛牛赢
而且他是显示每个点的父子结点,所以只需要两种颜色

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e6 + 7;
const int mod = 998244353;
int a[maxn];

int main(){
	memset(a,0,sizeof(a));
	int n;
	scanf("%d",&n);
	a[1] = 1;
	for(int i = 2;i <= n;i++){
		int x;
		scanf("%d",&x);
		if(a[x] == 1) a[i] == 2;
		else a[i] = 1;
	}
	ll cnt1 = 0,cnt2 = 0;
	for(int i = 1;i <= n;i++){
		if(a[i] == 1) cnt1++;
		else cnt2++;
	}
//	cout << cnt1 << " " << cnt2 << endl; 
	printf("%lld\n",cnt1*(cnt1-1)+cnt2*(cnt2-1));
	return 0; 
}

D - 音乐鉴赏

题目描述

链接:https://ac.nowcoder.com/acm/contest/3005/G
来源:牛客网
作为“音乐鉴赏”课的任课老师,你的课程作为刷学分好课一直受到广泛欢迎。但这一学期,学校制定了新的标准,你的课的优秀率(分数超过90分的人数)被限制在10%以下!
为了应对这个调整,你要求所有的同学都写了一篇论文,并使用随机算法打出了0-90之间的分数,分数可能不是整数。这里的随机是指,对于在[0,90]这个闭区间上的任何一对等长的区间,分数出现在其中的概率均是相同的。在期末的分数占比为百分之多少的时候,你的课程优秀率期望恰好在10%?保证所有同学的平时成绩都高于90分。

Input

输入第一行包含一个整数 n,保证n是10的倍数。
第二行包含 n 个整数,代表同学们的平时成绩。

Output

输出一行一个百分数,代表期末分数占比多少为合适。保留两位小数。

Sample Input

10
99 99 99 99 99 99 99 99 99 99

Sample Output

50.00%

说明

需要随机占比50%。
备注:
10 ≤ n ≤ 100000

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
const int mod = 998244353;
double a[maxn];
int n;
int check(double k){
	double ans = 0;
	for(int i = 1;i <= n;i++){
		double tmp = (90 - (1-k)*a[i])/k;
		if(tmp >= 90.00) continue;
		ans += (90.0 - tmp) / 90.0;
	}
	ans /= 1.0 * n;
	if(ans * 100 >= 10.0) return 1;
	return 0;
}

int main(){
	scanf("%d",&n);
	for(int i = 1;i <= n;i++) scanf("%lf",&a[i]);
	double l = 0.00,r = 1.00,m;
	for(int i = 0;i < 100;i++){
		m = (l + r) / 2;
		if(check(m)) l = m;
		else r = m;
	}
	printf("%.2lf%\n",m*100);
	return 0; 
}

(五)2020牛客寒假算法基础集训营5

A - 牛牛与牛妹的约会

题目描述

链接:https://ac.nowcoder.com/acm/contest/3006/D
来源:牛客网
牛牛在辛苦的一天的比赛之后,要去找牛妹玩,其实牛妹那天也在比赛。他为了找到牛妹,要尽快的从自己的比赛地到她的比赛地。
还记得吗,比赛地都是只在xx轴上的,所以两个人的坐标都满足y=0。牛牛除了可以以1单位距离/单位时间的速度移动任意时间以外,还可以花费1单位时间进行闪现。每次闪现时,如果当前他的坐标是x = k,他将闪现到x = sqrt[3]x (x开3次根)的位置。
请帮他算算,最短需要多少时间,他可以找到牛妹~

Input

输入数据包括多组用例,输入第一行包含一个数字T ( 1 ≤ T ≤ 5×10^5 ),表示数据组数。
接下来T行,每行包括两个整数a,b (∣a∣,∣b∣ ≤ 10^6 ),表示牛牛所在的位置和牛妹所在的位置。

Output

输出共T行,每行包括一个实数,表示牛牛所花费的最短时间。
如果你的答案是a,标准答案是b,当∣a−b∣≤10^−6 时,你的答案将被判定为正确。

Sample Input

2
3 -1
1 2

Sample Output

3.442249570
1.000000000

理解

只需判断闪现和走路哪个更省钱就行
两个同正同负就算差,异号就直接加牛妹到原点的距离

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;

int main() {
	int cas;
	scanf("%d",&cas);
	while(cas--){
		double cnt = 0;
		double a,b;
		scanf("%lf %lf",&a,&b);
		if((a >= 0 && b <= 0) || a <= 0 && b >= 0){
			if(a < 0) a = -a;
			while(a - pow(a,1.0/3) > 1){
				a = pow(a,1.0/3);
				cnt++;
			}
			cnt += a;
			cnt += fabs(b);
		}else{
			if(a < 0 && b < 0){
				a = -a;
				b = -b;
			}
			if(a < b) cnt = b - a;
			else{
				while(pow(a,1.0/3) >= b && a - pow(a,1.0/3) > 1){
					a = pow(a,1.0/3);
					cnt++;
				}
				double s1 = cnt + (a-b);	
				double s2 = cnt+1+abs(b-pow(a,1.0/3));
				cnt = min(s1,s2);
			}
		}
		printf("%.9lf\n",cnt);
	}
	return 0;
}

B - 碎碎念

题目描述

链接:https://ac.nowcoder.com/acm/contest/3006/F
来源:牛客网
在ACM比赛里,除了CE以外都是有效的提交。每一个提交都会有其评测的结果,或是AC,或是RJ(Rejected,包含各种不通过的情况)。往往一个人上去提交的时候,总有一个队友会坐在边上等着结果。那个人,往往都是只读题不写题的云选手~
牛牛战队里也有这样的云选手——牛能。当牛能看到有效提交得到了AC以后,都会大呼一声“你好能啊!”,反之,如果得到了RJ的话,就会化身为喷子,说xx句“你能不能行啊!”。大家比赛的都十分紧张,这样的大声呼喊未免会引起旁边队伍的注意。
当然牛牛战队交题的时候也很小心,一旦这一发出现了RJ,下一发有效提交一定能获得AC。
比赛结束了以后,旁边的一支队伍愤怒的跑过来说:你们比赛的时候吵不吵啊,一直在这大吼,吼了这么多句!
激烈的争吵引起了吃瓜群众的注意,吃瓜群众问道:吼了多少句啊,这么讨厌的吗
“啊……我也记不清了,大概是在[L,R]这个区间吧”
作为吃瓜群众的你,想根据这个信息算出,这个队伍有多少种有效提交结果序列的可能呢?

Input

输入数据包括单组数据、多组询问。输入第一行包含一个整数 x (2 ≤ x ≤ 100000),表示牛能在RJ状态下会说“你能不能行啊!”的句子数量。
第二行包括一个整数Q( 1 ≤ Q ≤ 10^5 ),表示询问数量。
接下来Q行,每行包括两个整数L,R ( 1 ≤ L ≤ R ≤ 100000),表示每次询问下句子的区间数。

Output

对于每组数据,在一行内输出一个整数,表示牛牛战队提交结果的可能性。由于结果可能很大,请对1000000007取模。

Sample Input

3
3
3 3
1 4
1 5

Sample Output

2
7
11

说明

第一组询问:可以是三个AC,或者一个RJ。
第二组询问:可以是1~4个AC,一个AC和一个RJ(共2种顺序),或者一个RJ。
第三组询问:可以有1~5个AC,两个AC和一个RJ(共3种顺序),一个AC和一个RJ(共2种顺序),或者一个RJ
备注:
AC RJ AC AC 和 AC AC AC RJ 虽然都是3个AC,1个RJ,但是因为提交顺序的不同,视为不同种类。

理解

由题意可知,AC后面可以是AC或者RJ,但是RJ后面一定是AC
根据这条去dp就ok

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
ll ac[maxn],rj[maxn],sum[maxn];

int main(){
	ll x;
	scanf("%lld",&x);
	ac[0] = 1; ac[1] = 1;
	rj[0] = 0; rj[1] = 0;
	for(int i = 2;i < maxn;i++){
		ac[i] = (ac[i-1] + rj[i-1])%mod;
		if(i >= x) rj[i] = ac[i-x] % mod;
	}
	sum[0] = 0;
	for(int i = 1;i < maxn;i++)
		sum[i] = (sum[i-1] + ac[i] + rj[i]) % mod;
	int q;
	scanf("%d",&q);
	while(q--){
		int l,r;
		scanf("%d %d",&l,&r);
		printf("%lld\n",(sum[r]-sum[l-1]+mod)%mod);
	}
	return 0;
}

(六)2020牛客寒假算法基础集训营6

A - 汉诺塔

题目描述

链接:https://ac.nowcoder.com/acm/contest/3007/C
来源:牛客网
现在你有 N 块矩形木板,第 i 块木板的尺寸是 Xi*Yi,你想用这些木板来玩汉诺塔的游戏。
我们知道玩汉诺塔游戏需要把若干木板按照上小下大的顺序堆叠在一起,但因为木板是矩形,所以有一个问题:
第 i 块木板能放在第 j 块木板上方当且仅当 Xi<Xj 且 Yi<Yj,于是你很可能没法把所有的木板按照一定的次序叠放起来。
你想把这些木板分为尽可能少的组,使得每组内的木板都能按照一定的次序叠放。
你需要给出任意一种合理的分组方案。
提醒:“任意”意味着你的答案不必和标准输出完全一致,只要正确即可。

Input

第一行,一个正整数 N
接下来 N 行,每行两个正整数表示 Xi 和 Yi
对于所有的数据,1≤N≤100,000,1≤Xi,Yi≤N,Xi 互不相等且 Yi 互不相等

Output

输出文件包含两行,第一行一个正整数,表示最少组数
第二行 N 个正整数,依次表示你的方案中每块木板分在了哪一组
组的编号必须是从 1 开始的连续整数

Sample Input

3
1 1
2 3
3 2

Sample Output

2
1 1 2

理解

就是导弹拦截(23333)还被卡了好久

AC代码

/*
5
1 4
2 3
3 2
5 1
4 5
*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 7;
const int mod = 1e9 + 7;
struct node{
	int y;
	int num;
	int z;
}a[maxn],idx[maxn];
int h[maxn];
int bmp(node a,node b){
	return a.num<b.num;
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++){
		int x,y;
		scanf("%d %d",&x,&y);
		a[x].y = y;
		a[x].num = i;
	}
	memset(idx,0,sizeof(idx));
	int cnt = 1;
	for(int i = 1;i <= n;i++){
		int fl = 0;
		int t = idx[i].y;
		if(idx[cnt].y > a[i].y){
			idx[++cnt].y = a[i].y;
			a[i].z = cnt;
			h[cnt] = idx[cnt].y;
		}else{
			int s = lower_bound(h+1,h+cnt+1,t) - h;
			for(int j = s;j <= cnt;j++){
				if(idx[j].y < a[i].y){
					idx[j].y = a[i].y;
					a[i].z = j;
					fl = 1;
					h[j] = idx[j].y;
					break;
				}
			}
		}
	}
	printf("%d\n",cnt);
	sort(a+1,a+n+1,bmp);
	printf("%d",a[1].z);
	for(int i = 2;i <= n;i++) printf(" %d",a[i].z);
	return 0;
}

(三)我的感想(略略略)

关于这个寒假的牛客练习

也就水水题,水水题的亚子,,,都是某些代佬带的好啊(此处圈一下紫妈)收获就是还有好多没学的东西也慢慢接触到了一些,毕竟自己还是很菜。突然很希望开学是怎么肥四,天梯啥的都延后了,是有了更多时间复习来着(对于本蒟蒻应该是预习)最近也有在洛谷水水题,(自然是被普及组卡爆)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值