C 盾与战锤
D 妄想集合
样例输入:
5 5
1 10 2 6 2
Ask 1 3
Quant 1 2 5
Ask 1 3
Quant 1 4 5
Ask 4 5
样例输出:
NO
YES
YES
主要是如何判断三角型,我们发现三角型的边长最多1e9;
而我们根据鸽巢原理发现只要不满足斐波那契数列的数据就一定可以构成三角形,而斐波那契数列在46位就爆了1e9;那么我们只需暴力排序检测集合元素个数小于46是否为斐波那契数列。
这样我们就需要一个数据结构来维护集合这里可以使用并查集或线段树维护每个集合的个数。
线段树:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N];
struct node
{
int l,r,sum;
bool flag;//flag保存该集合内每个子集内元素是否都大于46
}tr[N*4];
vector<int>G[N];
bool check(int l,int r)//判断集合内是否构成三角形
{
vector<int>num;
for(int i=l;i<=r;i++)
{
for(auto j:G[i])num.push_back(j);
}
if(num.size()<3)return false;
sort(num.begin(),num.end());
for(int i=2;i<num.size();i++)
{
if(num[i-2]+num[i-1]>num[i])return true;
}
return false;
}
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].flag=tr[u<<1].flag&tr[u<<1|1].flag;
}
void build(int u,int l,int r)
{
tr[u]={l,r,0,0};
if(l==r)
{
tr[u].sum=1;
G[l].push_back(a[l]);
return ;
}
else
{
int mid=(l+r)>>1;
build(u<<1,l,mid);build(u<<1|1,mid+1,r);
pushup(u);
}
}
int query(int u,int l,int r)
{
if(tr[u].l>=l&&r>=tr[u].r)
{
return tr[u].sum;
}
else
{
int sum=0;
int mid=(tr[u].l+tr[u].r)>>1;
if(mid>=l)sum+=query(u<<1,l,r);
if(mid<r)sum+=query(u<<1|1,l,r);
return sum;
}
}
void modify(int u,int l,int r,int x)
{
if(tr[u].flag)return ;
if(tr[u].l==tr[u].r)
{
tr[u].sum++;
if(tr[u].sum>46) tr[u].flag=1;
else G[tr[u].l].push_back(x);
}
else
{
int mid=(tr[u].l+tr[u].r)>>1;
if(r<=mid)modify(u<<1,l,r,x);
else if(l>mid)modify(u<<1|1,l,r,x);
else
{
modify(u<<1,l,r,x);
modify(u<<1|1,l,r,x);
}
pushup(u);
}
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
build(1,1,n);
while(m--)
{
char op[10];
int l,r;
scanf("%s %d %d",op,&l,&r);
if(op[0]=='A')
{
if(r-l+1>46)
{
puts("YES");
continue;
}
int cnt=query(1,l,r);
if(cnt>46)puts("YES");
else
{
if(check(l,r))puts("YES");
else puts("NO");
}
}
else
{
int x;
scanf("%d",&x);
modify(1,l,r,x);
}
}
}
并查集:
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],pre[N];
vector<int>G[N];
int findx(int x)
{
int r = x;
while (pre[r] != r) r = pre[r];
while (pre[x] != x)//不用路径压缩也可以
{
int t = pre[x];
pre[x] = r;
x = t;
}
return x;
}
bool check(int l,int r)
{
vector<int>num;
for(int i=l;i<=r;i++)
{
for(auto j:G[i])num.push_back(j);
}
if(num.size()<3)return false;
sort(num.begin(),num.end());
for(int i=2;i<num.size();i++)
{
if(num[i-2]+num[i-1]>num[i])return true;
}
return false;
}
int main()
{
int n,m;
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
G[i].push_back(a[i]);
}
for(int i=1;i<N;i++)pre[i]=i;
while(m--)
{
char op[10];
int l,r;
scanf("%s %d %d",op,&l,&r);
if(op[0]=='A')
{
if(r-l+1>46)
{
puts("YES");
continue;
}
int cnt=0;
for(int i=l;i<=r;i++)cnt+=G[i].size();
if(cnt>=46)puts("YES");
else
{
if(check(l,r))puts("YES");
else puts("NO");
}
}
else
{
int x;
scanf("%d",&x);
for(int i=l;i<=r;i=findx(i+1))
{
if(G[i].size()>=46)continue;
G[i].push_back(x);
if(G[i].size()==46)pre[i]=findx(i+1);//如果该集合元素大于46就直接与后面的点连边。
}
}
}
}
E:秘密构成