2023牛客寒假算法基础集训营6

 

目录

阿宁的倍数

阿宁的生成树

阿宁的整数配对

阿宁前往沙城


B-阿宁的倍数_2023牛客寒假算法基础集训营6 (nowcoder.com)

阿宁的倍数

题意是给你q个询问,有两种操作:

op==1时,在数组的末尾增加一个数

op==2时,查询ai(i>x)是ax的倍数的个数

分析:

这题我补完之后再想想才感觉其实是一眼题,为什么赛时做不出呢。。。

就是想要知道一段后缀里面有多少个ax的倍数

不妨就把每个数的倍数全部算出来(调和级数复杂度nlogn)

然后可以使用二分,把首尾的下标算出来即可。具体看代码

int b[N],c[N];
PAII query[N];
int a[N];
vector<int> v[N];
signed main(){
    IOS;
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {
    	int n,q;
    	cin>>n>>q;
    	for(int i=1;i<=n;i++)
    	{
			cin>>a[i];
			v[a[i]].push_back(i);
		}
    	int now=n,cnt=0;
    	for(int i=1;i<=q;i++)
    	{
    		int x,y;
    		cin>>x>>y;
			if(x==1)
			{
				now++;
				a[now]=y;
				v[y].push_back(now);
			}
			else if(x==2) query[++cnt]={y,now};//这里是ax
    	}
    	for(int i=1;i<=2e5;i++)
		{
			for(int j=i+i;j<=2e5;j+=i)   //调和级数 时间复杂度nlogn 
				v[i].insert(v[i].end(),v[j].begin(),v[j].end());//这里!!!!有个时间的优化
			sort(v[i].begin(),v[i].end());
		}
//		for(int i=1;i<=10;i++)
//		{
//			for(int j=0;j<v[i].size();j++)
//				cout<<v[i][j]<<" ";
//			cout<<"\n";
//		}
		for(int i=1;i<=cnt;i++)
		{
			int pos=query[i].first,cur=query[i].second;//此时查询原数组的x的值 
			int x=a[pos];//那个值上面的数字是多少 
			int p=upper_bound(v[x].begin(),v[x].end(),pos)-v[x].begin();
			int p1=upper_bound(v[x].begin(),v[x].end(),cur)-v[x].begin();
		//	cout<<pos<<" "<<cur<<" "<<x<<" "<<p<<endl; 
			cout<<p1-p<<"\n";
		}
    }
    return 0;
} 
/* 



*/

E-阿宁的生成树_2023牛客寒假算法基础集训营6 (nowcoder.com)

阿宁的生成树

构造题:

题意为给你一个完全图,完全图的边权是当(i<j)时,i+k<=j,边权为lcm(i,j)。否则时gcd(i,j)

分析:

对于任何连接,首选gcd,最大的gcd<=最小的lcm

对于1来说,所有数与1的gcd都是1,所以用1来构造。

令d[i]为编号i所被连接的边权值

所以大于等于k+2的数i d[i]==1

小于k+2的数i d[i]最大不会超过i

所以现在大于k+2的数全部被连通,可以通过暴力去减小【2,k+2)的边权值

注意:这里可以用双层循环的,这里有一个break的优化。我们开始枚举[2,k+2),只要枚举到后面有质数的地方就break掉,所以每一层循环不会太多。

题目中如果是不确定的数其实是不可以这样去枚举的,大概率会超时。这里可以枚举是因为,后面100%有质数,所以不会枚举到太大,就break了。也就是如果n很大,也可以不用枚举到n。

这个优化如果不加是会超时的。

下面看代码就好:

int d[N];
int ss(int n)
{
	if(n<2) return 0;
	for(int i=2;i<=n/i;i++)
		if(n%i==0) return 0;
	return 1;
}
signed main(){
    IOS;
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {
    	int n,k;
    	cin>>n>>k;
    	for(int i=2;i<=n;i++) d[i]=i;
    	for(int i=k+2;i<=n;i++) d[i]=1;
    	for(int i=2;i<k+2;i++)
    	{
			for(int j=i+k+1;j<=n;j++)
			{
				d[i]=min(d[i],__gcd(i,j));
				if(ss(j)) break;
			}
		}
		int sum=0;
		for(int i=2;i<=n;i++) sum+=d[i];
		cout<<sum; 
    }
    return 0;
} 

G-阿宁的整数配对_2023牛客寒假算法基础集训营6 (nowcoder.com)

阿宁的整数配对

题意是给长度为n的序列,选出k对,使其相乘再相加的值最大

分析:贪心

如果排序的话,肯定是相邻的组合最好。

但是有奇偶个数的存在,并不能直接这样算(这样算wa4...)

其实想到了相邻组合最好,本质上其实是正数越大越好 负数越小越好

所以我们使用优先队列去贪心的做

   //IOS;
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {
    	int n,k;
    	cin>>n>>k;
    	int x=0,y=0;
    	priority_queue<int> s;
    	priority_queue<int,vector<int>,greater<int> > t;
    	for(int i=1;i<=n;i++)
    	{
			cin>>a[i];
			if(a[i]>=0) s.push(a[i]);
			if(a[i]<0) t.push(a[i]);
		}
		int cnt=0;
		while(s.size())
		{
			if(s.size()==1) break;
			auto t=s.top();
			s.pop();
			auto t1=s.top();
			s.pop();
			res[++cnt]=t*t1;
		}
		while(t.size())
		{
			if(t.size()==1) break;
			auto t1=t.top();
			t.pop();
			auto t2=t.top();
			t.pop();
			res[++cnt]=t1*t2;
		}
		if(s.size()==1&&t.size()==1) res[++cnt]=s.top()*t.top();
		sort(res+1,res+cnt+1,greater<int>());
		int sum=0;
		for(int i=1;i<=k;i++) sum+=res[i];
		cout<<sum;
		
    }
    return 0;

I-阿宁前往沙城_2023牛客寒假算法基础集训营6 (nowcoder.com)

阿宁前往沙城

题意是给你一个无向图,从1号点出发到n号点的最短时间

可以在任何时间任何地点去使用魔法

魔法为任选两条路,摧毁其中一条和使另外一条时间变成1

分析:

最优的情况一定是走的路的权值全部变成1

因为是在任何时间任何地点都可以使用魔法并且没有任何消耗

先把边权全部当作1去跑一遍bfs,当作最短时间

只要有一条额外的边,我们的答案就是求出的这个最短时间

因为首先在1号点的时候,可以摧毁那条额外的边,然后使要走的那条变成1,接下来可以在走下一条路之前把之前走过的路摧毁,然后使得下一条路变成1。就这样一边走一边摧毁就可以

最坏情况就是道路是一条链,以上述的原则,只需要走第一条的时间+mn-1即可

const int N=2e6+10,M=5050,INF=1e18,mod=1e9+7;
int h[N],ne[N],e[N],w[N],dist[N];
int idx,n,m;
void add(int a,int b,int c)
{
	e[idx]=b;
	w[idx]=c;
	ne[idx]=h[a];
	h[a]=idx++;
}
void bfs()
{
	queue<int> q;
	q.push(1);
	dist[1]=0;
	while(q.size())
	{
		auto t=q.front();
		q.pop();
		for(int i=h[t];i!=-1;i=ne[i])
		{
			int j=e[i];
			if(dist[j]==-1)
			{
				dist[j]=dist[t]+1;
				q.push(j); 
			}
		}
	}
}
signed main(){
    IOS;
    int T;
    T=1;
    //cin>>T;
    while(T--)
    {
    	int n,m;
    	cin>>n>>m;
    	for(int i=1;i<=n;i++) h[i]=-1,dist[i]=-1;
    	for(int i=1;i<=m;i++)
    	{
			int a,b,c;
			cin>>a>>b>>c;
			add(a,b,c);
			add(b,a,c);
		}
		bfs();
		int mn=dist[n];
		if(m>mn)
		{
			cout<<mn;
			continue;
		}
		int ww;
		for(int i=h[1];i!=-1;i=ne[i]) ww=w[i];
		cout<<mn-1+ww;
    }
    return 0;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值