原题链接:G-鸡格线_2023牛客寒假算法基础集训营1 (nowcoder.com)
题意:
你有一个长为n的数组a,你需要支持以下两种操作:
1、输入l,r,k,对区间[l,r]中所有数字执行 a[i]=f[a[i]] 操作k次(式中等号表示赋值操作),之中f(x)=round(10*sqrt(x)),round为四舍五入函数。
2、输出当前数组所有数字的和。
你需要正确处理m次这样的操作。
范围: n,m,l,r,k均是1-1e5 ai为0-1e9
由f(x)=round(10*sqrt(x))可以看出最后f(x)会在(x=0||x=99||x=100)以后保持不变,而ai的范围为0-1e9也就是说最多进行10几次操作就可以收敛到固定值。
那么在修改l-r范围时只需要将未收敛的值经行k次或者更少的操作就行。问题就转变成如何在l-r区间找到未收敛的值。
方案1:利用set存目前还未收敛的值的下标利用set的Lower bond和erase的方法解决 l,r区间每次在set里用lowerbound找大于l小于r的下标进行修改操作,然后判断是否收敛如果收敛的话将这个下标删除。最后更新l知道找到的下标大于r。
这种方法最简单最直观的就可以算出结果。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
#define PII pair<int,int>
const int N=2e5+10;
const int mod=1e9+7;
int lowbit(int x){return x&(-x);}
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int a[N];
int fun(int x)
{
return round(10.0*sqrt(1.0*x));
}
void solve()
{
// freopen("D:\\c++\\in","r",stdin);
// freopen("D:\\c++\\out1","w",stdout);
int n,m;
cin>>n>>m;
int sum=0;
set<int>se;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(fun(a[i])!=a[i])
{
se.insert(i);
}
sum+=a[i];
}
se.insert(n+1);(作为判断的最后一位)
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int l,r,k;
cin>>l>>r>>k;
while (1)
{
int pos= *se.lower_bound(l);
if(pos>r)
{
break;
}
for(int i=0;i< min(k,20ll);i++)
{
int d= fun(a[pos]);
sum+=(d-a[pos]);
a[pos]=d;
}
if(a[pos]== fun(a[pos]))
{
se.erase(pos);
}
l=pos+1;
}
}
else
{
cout<<sum<<endl;
}
}
}
signed main()
{
// IOS
// int _;cin>>_;while(_--)
solve();
return 0;
}
/* 2 2 1 1 1
*
*
*/
方案2:线段数维护区间建树时多一个max和min可以观察出if(max<=100&&min>=99)就可以知道这个区间已经修改完了不需要再修改,多出来0这个数可以将a[i]==0这个点的max和min修改成100不影响结果。
比较公式的做法。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
#define PII pair<int,int>
const int N=2e5+10;
const int mod=1e9+7;
int lowbit(int x){return x&(-x);}
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int a[N];
int fun(int x)
{
return round(10.0*sqrt(1.0*x));
}
struct node
{
int l,r,minn,maxx,sum;
}tree[N*4];
void pushUp(int now)
{
tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
tree[now].maxx= max(tree[now<<1].maxx,tree[now<<1|1].maxx);
tree[now].minn= min(tree[now<<1].minn,tree[now<<1|1].minn);
}
void build(int now,int l,int r)
{
tree[now].l=l;
tree[now].r=r;
if(l==r)
{
tree[now].maxx=a[l];
tree[now].minn=a[l];
tree[now].sum=a[l];
if(a[l]==0) tree[now].maxx=tree[now].minn=100;
return;
}
int mid=l+r>>1;
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
pushUp(now);
}
void change(int now,int l,int r,int k)
{
// cout<<now<<" "<<tree[now].l<<" "<<tree[now].r<<endl;
if(tree[now].maxx<=100&&tree[now].minn>=99)
{
return;
}
if(tree[now].l==tree[now].r)
{
for(int i=0;i<k;i++)
{
int d=fun(tree[now].maxx);
if(d==tree[now].maxx) return;
else tree[now].minn=tree[now].sum=tree[now].maxx= d;
}
return;
}
int mid=tree[now].l+tree[now].r>>1;
if(l<=mid)
{
change(now<<1,l,r,k);
}
if(r>mid)
{
change(now<<1|1,l,r,k);
}
pushUp(now);
}
void solve()
{
// freopen("D:\\c++\\in","r",stdin);
// freopen("D:\\c++\\out1","w",stdout);
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
}
build(1,1,n);
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int l,r,k;
cin>>l>>r>>k;
change(1,l,r,k);
}
else
{
cout<<tree[1].sum<<endl;
}
}
}
signed main()
{
// IOS
// int _;cin>>_;while(_--)
solve();
return 0;
}
/* 2 2 1 1 1
*
*
*/
方案3:并查集解决。当一个值a[i]已经收敛完成可以看看a[i+1]和a[i-1]是否也收敛完毕
规定合并方向(我是向左)
如果左边的收敛完毕则:fa[find(i)]=find(i-1)。
如果右边的收敛完毕则:fa[find(i+1)]=find(i)。
最后根据合并方向进行跳跃直到目前的下标<l即可。
#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
#define int long long
#define PII pair<int,int>
const int N=2e5+10;
const int mod=1e9+7;
int lowbit(int x){return x&(-x);}
int dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int a[N];
int fun(int x){return round(10.0*sqrt(1.0*x));}
int fa[N];
int n,m;
void init()
{
for(int i=1;i<=n;i++) fa[i]=i;
}
int find(int x)
{
return x==fa[x]?fa[x]: fa[x]= find(fa[x]);
}
void solve()
{
// freopen("D:\\c++\\in","r",stdin);
// freopen("D:\\c++\\out1","w",stdout);
cin>>n>>m;
int sum=0;
for(int i=1;i<=n;i++)
{
cin>>a[i];
sum+=a[i];
}
init();
while(m--)
{
int op;
cin>>op;
if(op==1)
{
int l,r,k;
cin>>l>>r>>k;
if(find(r)== find(l)) continue;
while(r>=l)
{
int t=a[r];
for(int i=0;i<k;i++)
{
int d= fun(t);
if(d==t) break;
t=d;
}
sum+=(t-a[r]);
a[r]=t;
if(fun(a[r])==a[r])
{
if(r-1>=1&& fun(a[r-1])== a[r-1]) fa[find(r)]= find(r-1);
if(r+1<=n&& fun(a[r+1])==a[r+1]) fa[find(r+1)]= find(r);
}
r= find(r)-1;
}
}
else
{
cout<<sum<<endl;
}
}
}
signed main()
{
// IOS
// int _;cin>>_;while(_--)
solve();
return 0;
}
/* 2 2 1 1 1
*
*
*/