2021牛客寒假算法基础集训营2

https://ac.nowcoder.com/acm/contest/9982#question

D-牛牛与整除分块

大意:构造集合S={x: x =N/i, i=1,2,3...N},求n/x在S中第几大(S去重降序排序)
思路:先看n=10时S中有哪些数。

多列出几个数可以发现当时,,当时,x是多少,那么n/x就第几大。
所以当时,我们先求出n/x在右边集合中排第几,在加上左边的集合大小(左边集合大小为)就是答案。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

void solve()
{
	int t,n,x,ans;
	cin >> t;
	while(t--){
		ans = 0;
		cin >> n >> x;
		int m = sqrt(n);
		if(x <= m){
			ans = x;
		}
		else{
			int k = n/x;
			int h = n/(m+1);
			ans = m+h-k+1;
		}
		cout << ans << endl;
	}
}

int main()
{
	solve();
	return 0;
}

E-牛牛与跷跷板:双指针+最短路(bfs/dfs/dijkstra)

思路:看题很容易联想的最短路,但关键是如何将这个图连起来。分成两种情况。
1)两块跷跷板在同一平面,那么比较相邻两块,左边的r等于右边的l就连起来。
2)两块不在同一平面,而是在上下相邻平面,利用双指针l,r,  l指向下平面最左边那块,r指向下平面最右边那块,每一次让l往r移动,如果node[l].l >= node[i].r,那么l和l后面的跷跷板都不可能和i连接(可以画图看看),那么再来判断第i+1块,同时让j--,因为第i+1块和第i块是可能连接同一块的。如果node[l].r <= node[i].l,那l后面的跷跷板还是有可能和i连接的。
连接完图后,因为相邻跷跷板的路程是1,bfs,dfs,dij都可以解决最短路。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

struct Node{
	int y,l,r,num;
}node[N];

vector<int>a[N];
int n,vis[N],d[N];

bool cmp(Node a,Node b)
{
	if(a.y == b.y) return a.l < b.l;
	return a.y < b.y;
}


void bfs()
{
	queue<int>q;
	q.push(1);
	vis[1] = 1;
	d[1] = 0;
	while(!q.empty()){
	//	cout << 1 <<endl;
		int t = q.front();
		q.pop();
		if(t == n) break;
		for(int i = 0; i < a[t].size(); i++){
			if(vis[a[t][i]]) continue;
			vis[a[t][i]] = 1;
			q.push(a[t][i]);
			d[a[t][i]] = d[t] + 1;
		}
	}
	printf("%d",d[n]);
}

void solve()
{
	scanf("%d",&n);
	for(int i = 1; i <= n; i++){
		int y,l,r;
		scanf("%d%d%d",&y,&l,&r);
		node[i].y = y;
		node[i].l = l;
		node[i].r = r;
		node[i].num = i;
	}
	sort(node+1,node+n+1,cmp);
	int l = 0,r = 0;
	for(int i = 1; i <= n; i++){
		if(node[i].r == node[i+1].l && node[i].y == node[i+1].y) {
			a[node[i].num].push_back(node[i+1].num);
			a[node[i+1].num].push_back(node[i].num);
		}
		while(l <= n && node[l].y == node[i].y) l++;
		r = max(l,r);
		while(r <= n && node[r].y == node[l].y) r++;
		while(l < r){
			//cout << l << endl;
			if(node[l].l >= node[i].r){
				//l++;
				break;
			}
			if(node[l].r <= node[i].l){
				l++;
				continue;
			}
			a[node[l].num].push_back(node[i].num);
			a[node[i].num].push_back(node[l].num);
			l++;
		} 
		if(l > 1) l--;
	}
	bfs();
}

int main()
{
	solve();
	return 0;
}

F-牛牛与交换排序:构造数据结构

大意:牛牛有一个数组,里面的元素为1-n的一个排列,规定一个k为区间的大小,牛牛可以去翻转区间大小为k的子数组,但是每一次翻转的区间一定要在前一个区间的右边,问是否存在k,使得经过若干次翻转后,区间变得有序?
思路:因为每一次翻转的区间一定要在前一个区间的右边,并且数组里面的元素为1-n的一个排列,有序后数组满足a[i]=i,所以在a[i]不等于i的时候,一定要把等于i的数交换到位置i上去,否则下一次翻转的时候区间不可能包含位置i。在第一次进行判断的时候就能确定区间的大小k,然后依次判断翻转后a[i]是否等于i,不等于就再判断从a[i+k-]是否等于i(因为区间大小固定为k),不等于则不可能翻转成有序,相等则再继续往后判断。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

int xb[1000005],a[1000005],pre[1000005];
void solve()
{
	int n,x,k = 0;
	scanf("%d",&n);
	for(int i = 1; i <= n; i++){
		scanf("%d",&x);
		xb[x] = i;
		a[i] = x;
		if(x == i){
			pre[i] = pre[i-1]+1;
		}
		else pre[i] = pre[i-1];
	}
	for(int i = 1; i <= n; i++){
		if(a[i] != i){
			k = xb[i]-i+1;//由第一个不相等的地方确定k的大小。
			break;
		}
	}
	int flag = 0;
	for(int i = 1; i <= n; i++){
		if(a[i] != i){
			if(i+k-1 > n || a[i+k-1] != i){//不相等答案为no
				flag = 1;
				break;
			}
			int l = i,r = i+k-1;
            while(l < r){
                int temp1,temp2;
                temp1 = a[l];
                temp2 = xb[a[l]];
                xb[a[l]] = xb[a[r]];
                xb[a[r]] = temp2;
                a[l] = a[r];
                a[r] = temp1;
                l++;
                r--;
            }
		}
	}
	if(flag) printf("no\n");
	else {
		printf("yes\n");
		if(k) printf("%d",k);
		else printf("%d",k+1);
	}
}

int main()
{
	solve();
	return 0;
}

G-牛牛与比赛颁奖:差分+离散化

思路:看完题首先要想到差分,再看数据范围,1e9,不能开这么大的数组,所以考虑用离散化。将m个l,r离散化存入结构体当中。
最后巧妙的运用过题数和过题人数算排名。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 100005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

struct Node{
	int xb,num;
}node[N*2];

int cnt[N],pre[N];cnt[i]表示过了i题有多少人。
bool cmp(Node a,Node b)
{
	if(a.xb == b.xb) return a.num < b.num;
	return a.xb < b.xb;
}

void solve()
{
	int n,m,l,r;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= m; i++){
		scanf("%d%d",&l,&r);
		node[i].xb = l;
		node[i].num = 1;
		node[i+m].xb = r+1;
		node[i+m].num = -1;
	}
	sort(node+1,node+m+m+1,cmp);
	int mmax = 0,sum = 0;
	for(int i = 1; i <= m+m; i++){
		if(node[i].xb - node[i-1].xb != 0){
			cnt[sum] += node[i].xb - node[i-1].xb;
		}
		sum += node[i].num;
		mmax = max(mmax,sum);
	}
	int j,y,t;
	j = y = t = -1;
	for(int i = mmax; i >= 0; i--){
		pre[i] = pre[i+1] + cnt[i];
		if(pre[i] >= (n+9)/10 && j == -1) j = i;
		if(pre[i] >= (n+3)/4 && y == -1) y = i;
		if(pre[i] >= (n+1)/2 && t == -1) t = i;
	//	cout << pre[i] << endl;
	}
	j = max(j,1);
	y = max(y,1);
	t = max(t,1);
//	cout << j << " " << y << " " << t << " " << endl; 
	printf("%d %d %d",pre[y+1]-pre[mmax],pre[y]-pre[j],pre[t]-pre[y]);
}

int main()
{
	solve();
	return 0;
}

I-牛牛与质因数:dfs/dp/输入筛

大意:定义函数F(x) ,它表示将x做质因数分解后得到的数字从小到大升序排列,然后将其“拼接”成一个大整数。求
思路:当n=10时,F(2) = 2,F(3) = 3,F(4) = 22,F(5) = 5,F(6) = 23,F(7) = 7,F(8) = 222,F(9) = 33,F(10) = 25
其实也就是2-10内的质因数的组合,组合的质因数相乘的结果要小于等于n ,现在考虑如何拼接,假设现在已经拼接成res,把质数x拼接上去,拼接完后res = res*10^cnt[x]+x,其中cnt[x]表示x的位数,而不是直接乘上十。2-n内质因数的组合可以用dfs来实现,剪枝是相乘结果大于n则回溯,即return。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

int ans = 0,n,cnt;
ll sum = 0;
int v[10000005],prime[10000005],vis[10000005];
void init()
{
    cnt = 0;
    for(int i = 2; i <= n; i++){
        if(!v[i]){
            cnt++;
            prime[cnt] = i;
            v[i] = i;
        }
        for(int j = 1; j <= cnt; j++){
            if(1ll*prime[j]*1ll*i > n) break;
            v[i * prime[j]] = prime[j];
            if(i % prime[j] == 0) break;
        }
    }
}

int ws(int x)
{
	int t = 0;
	while(x){
		t++;
		x /= 10;
	}
	return t;
}

int qpow(int a,int b)
{
	int res = 1;
	while(b){
		if(b&1) res = 1ll*res*a%mod;
		b >>= 1;
		a = 1ll*a*a%mod;
	}
	return res;
}

void dfs(int cur,ll res,ll temp)
{
	sum = (sum+res)%mod;
//	cout << res << " " << temp << endl;
//	cout << cur << " " << res << endl;
	for(int i = cur; i <= cnt; i++){
		if(temp*prime[i] <= n)
	    	dfs(i,(res*qpow(10,ws(prime[i]))+prime[i])%mod,temp*prime[i]);
	    else break;
	}
}

void solve()
{
	scanf("%d",&n);
	init();
	for(int i = 1; i <= cnt; i++){
	//	cout << prime[i] << "\n";
		dfs(i,1ll*prime[i],1ll*prime[i]);
	}
	printf("%lld\n",sum%mod);
}

int main()
{
	solve();
	return 0;
}
输入筛
思路:f[1500] = 223555,去最大的素因子,f[1500/5] = f[300] = 22355,所以f[1500] = f[300]*10+5,同理f[300] = f[60]*10+5.
这样在筛素数时,每次将素数i作为最大素因子,利用f[i] = f[j/i]*10^ws(i)+i求f[i]。倘若某个数的最大素因子不是i也没有关系,在后面比i大的素数去筛的时候会更新。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 5000005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

bool vis[N];
int f[N];
ll ans = 0;

int ws(int x)
{
    int t = 0;
    while(x){
        t++;
        x /= 10;
    }
    return t;
}
 
int qpow(int a,int b)
{
    int res = 1;
    while(b){
        if(b&1) res = 1ll*res*a%mod;
        b >>= 1;
        a = 1ll*a*a%mod;
    }
    return res;
}

void solve()
{
	int n;
	scanf("%d",&n);
	for(int i = 2; i <= n; i++){
		if(!vis[i]){
			ans = (ans+i)%mod;
			f[i] = i;
			for(int j = i+i; j <= n; j+=i){
				if(f[j/i] != 0)	{
					int t = qpow(10,ws(i))*f[j/i]+i;
					ans = (ans+t)%mod;
					f[j] = t;
				}
				vis[j] = true;
			}
		}
	}
	printf("%d\n",ans);
}

int main()
{
	solve();
	return 0;
}




J-牛牛相成为hacker:构造题

思路:根据题意很容易想到用斐波那契数列来构造,但是题目要求的数的大小不能超过1e9,而斐波那契数列在长度为40是就超过了1e9,所以当n<=39时,构造斐波那契数列就行,那n>=40怎么办呢,同样前面构造斐波那契数列,后面补1,但前面的两个1不要。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>
#include <queue>
#include <string.h>
#include <cmath>
#include <bitset> 
#include <set>
#include <map>
#include <list>
#define ull unsigned long long
#define ll long long
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const int N = 500005;
const ll ds = 1e15+7;
const double p1 = 3.141592653589793238462643383;

using namespace std;

ll f[105];
void solve()
{
	int n;
	f[1] = 1,f[2] = 1;
	for(int i = 3; i <= 39; i++){
		f[i] = f[i-1]+f[i-2]; 
	}
	scanf("%d",&n);
	if(n <= 37){
		for(int i = 3; i <= n+2; i++){
			printf("%lld ",f[i]);
		}
	}
	else{
		for(int i = 3; i <= 39; i++){
			printf("%lld ",f[i]);
		}
		for(int i = 40; i <= n+2; i++){
			printf("%d ",1);
		}
	}
}

int main()
{
	solve();
	return 0;
}



全部评论

相关推荐

10-28 14:42
门头沟学院 Java
watermelon1124:因为嵌入式炸了
点赞 评论 收藏
分享
专心打鱼:互联网搬运工,贴子都要偷
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务