题意:开始有 n 个可重集合,开始时每一个集合中都有一个数,有 m个操作。
- Quant l r x:往编号在 l∼r 的每个集合中加入一个数 x。
- Ask l r:询问能否从 l∼r 的集合中取出三个数使得他们能作为边长组成一个三角形(即最小两个和要大于最大的)。
分析:首先有一个结论:对于权值<=f(n)的集合,如果集合内至少有n+1个数字,那么必定可以找出三条边构成三角形,参考斐波那契数列1 2 3 5 8... 。 于是可以考虑线段树暴力修改。对于修改,单个集合元素个数>50就不必再加数字了;对于查询,区间内集合的元素个数之和>50必然可以构成三角形,否则只需要check一遍。时间复杂度为O(50nlogn+50logn) 。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int a[maxn];
int val[maxn<<2],flag[maxn<<2];
vector<int>v[maxn],ans;
int f;
void pushup(int rt)
{
// val[rt]=val[rt<<1]+val[rt<<1|1];
flag[rt]=flag[rt<<1]&flag[rt<<1|1];
}
void update(int L,int R,int x,int l,int r,int rt)
{
if(flag[rt]) return;//rt之下的所有单个集合的size都到了50
if(l==r)
{
v[l].push_back(x);
if(v[l].size()>50) flag[rt]=1;
return;
}
int m=(l+r)>>1;
if(L<=m) update(L,R,x,l,m,rt<<1);
if(R>m) update(L,R,x,m+1,r,rt<<1|1);
pushup(rt);
}
void query(int L,int R,int l,int r,int rt)
{
if(ans.size()>50) return;//总数大于50必然成立 否则50logn放入ans再check
if(l==r)
{
for(int i=0;i<v[l].size();i++)
{
ans.push_back(v[l][i]);
}
return;
}
int m=(l+r)>>1;
if(L<=m) query(L,R,l,m,rt<<1);
if(m<R) query(L,R,m+1,r,rt<<1|1);
}
void check()
{
sort(ans.begin(),ans.end());
for(int i=2;i<ans.size();i++)
{
if(ans[i-2]+ans[i-1]>ans[i]) f=1;
}
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
v[i].push_back(a[i]);
}
while(m--)
{
char op[10];
cin>>op;
int l,r,x;
if(op[0]=='Q')
{
cin>>l>>r>>x;
update(l,r,x,1,n,1);
}
else
{
ans.clear();
f=0;
cin>>l>>r;
query(l,r,1,n,1);
if(ans.size()>50)
{
puts("YES");
}
else
{
check();
if(f) puts("YES");
else puts("NO");
}
}
}
}
知道结论后直接暴力修改+continue显然更易想到,但很可惜t了16.6%。考虑并查集优化,从而跳过大小>50的集合。时间复杂度为q50α(n) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int fa[maxn],a[maxn];
vector<int>v[maxn],ans;
int find(int x){ //找x的老大
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>a[i];
v[i].push_back(a[i]);
fa[i]=i;
}
fa[n+1]=n+1;//不加会内存超限
while(m--)
{
char op[10];
cin>>op;
int l,r,x;
if(op[0]=='Q')
{
cin>>l>>r>>x;
int now=find(l);
while(now<=r)//用并查集跳过大小>50的集合
{
v[now].push_back(x);
if(v[now].size()>50) fa[now]=now+1;
now=find(now+1);
// cout<<now<<endl;
}
}
else
{
cin>>l>>r;
ans.clear();
for(int i=l;i<=r;i++)
{
if(ans.size()>50) break;
for(int j=0;j<v[i].size();j++)
{
if(ans.size()>50) break;
ans.push_back(v[i][j]);
}
}
int f=0;
if(ans.size()>50) f=1;
sort(ans.begin(),ans.end());
for(int i=2;i<ans.size();i++)
{
if(ans[i-2]+ans[i-1]>ans[i]) f=1;
}
if(f) puts("YES");
else puts("NO");
}
}
}