目录
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;