题目链接:E. Sum Queries?
题意:给定一个有n(1<=n<=2*10^5)个元素a(1<=a<=10^9)的数组,进行m(1<=n<=2*10^5)次操作,每次执行以下两种操作之一
1.修改某个元素值,
2.询问区间[l,r]内和最小的集合,使得集合数的和对应十进制每一位都能出现在集合中某个数对应位置上
题解:这种格式一看就是线段树,但是看到操作2的询问第一时间就感觉很懵,但我们手动模拟几次就会发现规律,只要任何两个数有相同的某一位都不为0,那这种两个数组成的集合就一定是unbalanced,因为这种情况下要保持balanced就必须多一个数,并且产生进位,而进位又会导致下一位的不平衡,想通这里了就好办了,就是线段树维护区间某一位都不为0的最小对,可以开二维数组,第二维存对应位数在区间内对应的最小数即可,细节比较多,特别是询问部分和区间合并,要分在一个完整区间内的unbalanced对,和不同区间内的,在完整区间内的直接取就行,不同区间内的存个res数组维护下即可,具体看代码及注释
#include<iostream>
#include<stack>
#include<list>
#include<set>
#include<vector>
#include<algorithm>
#include<math.h>
#include<numeric>
#include<map>
#include<cstring>
#include<queue>
#include<iomanip>
#include<cmath>
#include<queue>
#include <bitset>
#include<unordered_map>
#ifndef local
#define endl '\n'
#endif */
#define mkp make_pair
using namespace std;
using std::bitset;
typedef long long ll;
typedef long double ld;
const int inf=0x3f3f3f3f;
const ll MAXN=2e6+10;
const ll N=1e5+100;
const ll mod=1e9+7;
const ll hash_p1=1610612741;
const ll hash_p2=805306457;
const ll hash_p3=402653189;
//-----------------------------------------------------------------------------------------------------------------*/
// ll head[MAXN],net[MAXN],to[MAXN],edge[MAXN]/*流量*/,cost[MAXN]//费用;
/*
void add(ll u,ll v,ll w,ll s){
to[++cnt]=v;net[cnt]=head[u];edge[cnt]=w;cost[cnt]=s;head[u]=cnt;
to[++cnt]=u;net[cnt]=head[v];edge[cnt]=0;cost[cnt]=-s;head[v]=cnt;
}
struct elemt{
int p,v;
};
-----------------------------------
求[1,MAXN]组合式和逆元
ll mi(ll a,ll b){
ll res=1;
while(b){
if(b%2){
res=res*a%mod;
}
a=a*a%mod;
}
return res;
}
ll fac[MAXN],inv[MAXN]
fac[0]=1;inv[0]=1;
for(int i=1;i<=MAXN;i){
fac[i]=(fac[i-1]*i)%mod;
inv[i]=mi(fac[i],mod-2);
}
ll C(int m,int n){//组合式C(m,n);
if(!n){
return 1;
}
return fac[m]*(inv[n]*inv[m*-n]%mod)%mod;
}
---------------------------------
unordered_map<int,int>mp;
//优先队列默认小顶堆 , greater<int> --小顶堆 less<int> --大顶堆
priority_queue<elemt,vector<elemt>,comp>q;
struct comp{
public:
bool operator()(elemt v1,elemt v2){
return v1.v<v2.v;
}
};
set<int>::iterator it=st.begin();
*/
// vector<vector<int>>edge; 二维虚拟储存坐标
//-----------------------------------------------------------------------------------------------------------------*/
//map<int,bool>mp[N];
ll a[2*N];
ll p[20];//存10的i次幂
void init(){
p[0]=1;
for(int i=1;i<=11;i++){
p[i]=p[i-1]*10ll;
}
}
ll minn[8*N][10];//线段树对应区间内各位置上数对应的最小值
ll sum[8*N];//对应区间内已经存在的unbalanced
void pushin(int rt){//合并区间
sum[rt]=0;
if(sum[2*rt]&&sum[2*rt+1]){
sum[rt]=min(sum[2*rt],sum[2*rt+1]);
}
else if(sum[2*rt]){
sum[rt]=sum[2*rt];
}
else if(sum[2*rt+1]){
sum[rt]=sum[2*rt+1];
}
for(int i=0;i<=9;i++){
if(minn[2*rt][i]&&minn[2*rt+1][i]){//有unbalanced出现
if(sum[rt]){
sum[rt]=min(sum[rt],minn[2*rt][i]+minn[2*rt+1][i]);//如果区间内已经有了,就取最小的一组
}
else{
sum[rt]=minn[2*rt][i]+minn[2*rt+1][i];
}
minn[rt][i]=min(minn[2*rt][i],minn[2*rt+1][i]);//别忘记标记也要更新
}
else if(minn[2*rt][i]){
minn[rt][i]=minn[2*rt][i];
}
else if(minn[2*rt+1][i]){
minn[rt][i]=minn[2*rt+1][i];
}
else{//否则就改为0,说明无标记
minn[rt][i]=0;
}
}
}
void build(int l,int r,int rt){
if(l==r){
for(int i=0;i<=9;i++){//枚举数位
if((a[l]%p[i+1])/p[i]){//该数位上有数就更新
minn[rt][i]=a[l];
}
else{
minn[rt][i]=0;
}
}
return ;
}
int mid=(l+r)/2;
build(l,mid,2*rt);
build(mid+1,r,2*rt+1);
pushin(rt);
}
void update(int l,int r,int rt,int x,ll v){
if(l==r){
for(int i=0;i<=9;i++){//枚举数位
if((v%p[i+1])/p[i]){//该数位上有数就更新
minn[rt][i]=v;
}
else{
minn[rt][i]=0;
}
}
return ;
}
int mid=(l+r)/2;
if(x<=mid){
update(l,mid,2*rt,x,v);
}
else{
update(mid+1,r,2*rt+1,x,v);
}
pushin(rt);
}
ll res[20];//储存查询中各数位结果
ll ans=1e18;//c存储答案
void query(int l,int r,int rt,int le,int ri){
if(le<=l&&ri>=r){
if(sum[rt]){//如果区间内有unbalanced
ans=min(ans,sum[rt]);
}
for(int i=0;i<=9;i++){
if(minn[rt][i]){
if(!res[i]){
res[i]=minn[rt][i];
}
else{
ans=min(ans,res[i]+minn[rt][i]);//有unbalanced出现,更新答案,
res[i]=min(res[i],minn[rt][i]);
}
}
}
return;
}
int mid=(l+r)/2;
if(le<=mid){
query(l,mid,2*rt,le,ri);
}
if(ri>mid){
query(mid+1,r,2*rt+1,le,ri);
}
}
int main(){
/*cout<<setiosflags(ios::fixed)<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(不含整数部分)*/
/*cout<<setprecision(8)<<ans<<endl;//输出ans(float)格式控制为8位小数(含整数部分)*/
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);//同步流
init();//预处理出10的i次幂
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,n,1);
while(m--){
int k,l,r;
cin>>k>>l>>r;
if(k==1){
update(1,n,1,l,r);
}
else{
ans=1e18;//清空之前答案
memset(res,0,sizeof(res));//清空之前记录
query(1,n,1,l,r);
if(ans==1e18){
ans=-1;//未查找到unbalanced
}
cout<<ans<<endl;
}
}
return 0;
}