数据结构专题
线段树
Can you answer these queries?
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027
一般线段树区间修改是通过传递lazy标记 但是这题传递lazy标记就不太可行 我们可以发现 最大数据最多也只能sqrt七次(sqrt(1)=1) 意思就是最多七次操作之后值就不变 所以我们直接暴力修改到叶子节点 然后加入区间的所有数全为1之后 (sum[rt]=r-l+1)就直接退出当前递归 然后这题有个很气人的坑点 就是输入的a不一定大于b
/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 1e5+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
ll sum[maxn<<2];
void PushUp(int rt)
{
sum[rt]=sum[rt<<1|1]+sum[rt<<1];
}
void build(int l,int r,int rt)
{
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
int m = (l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
PushUp(rt);
}
void update(int L,int R,int l,int r,int rt)
{
if(l==r)
{
sum[rt]=(ll)(sqrt(sum[rt]));
return;
}
if(sum[rt]==(r-l+1)) return;
int m=(l+r)>>1;
if(L<=m) update(L,R,l,m,rt<<1);
if(m<R) update(L,R,m+1,r,rt<<1|1);
PushUp(rt);
}
ll query(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return sum[rt];
int m=(l+r)>>1;
ll ans=0;
if(L<=m) ans+=query(L,R,l,m,rt<<1);
if(m<R) ans+=query(L,R,m+1,r,rt<<1|1);
return ans;
}
int main()
{
int n;
int tot=0;
while(scanf("%d",&n)==1)
{
memset(sum,0,sizeof(sum));
printf("Case #%d:\n",++tot);
build(1,n,1);
ll t;
scanf("%lld",&t);
while(t--)
{
int num;
scanf("%d",&num);
if(num==0)
{
int a,b;
scanf("%d %d",&a,&b);
if(a>b) swap(a,b);
update(a,b,1,n,1);
}
else
{
int a,b;
scanf("%d %d",&a,&b);
if(a>b) swap(a,b);
printf("%lld\n",query(a,b,1,n,1));
}
}
printf("\n");
}
return 0;
}
Tunnel Warfare
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1540
D:摧毁x
R:重建上一个被摧毁的x
Q:查询x左右两边被摧毁的城市中连续的未被摧毁的城市的个数
这一题有两种做法
1.维护两个数组 第一个用于查询右边离x最近的被摧毁的城市 第二个用于查询左边离x最近的被摧毁的城市
我们可以初始化查询max的数组全为n+1 查询min的数组全为0 如果x城市被摧毁 就更新x对应的两个数组成x
然后查询q 查询1~q的最大值 q~n的最小值 ans=minn-maxx-1
/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int summin[maxn<<2],summax[maxn<<2];
int rebuild[maxn<<2];
int n,m,tot;
void build(int l,int r,int rt)
{
if(l==r)
{
summin[rt]=n+1;
summax[rt]=0;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
summin[rt]=min(summin[rt<<1|1],summin[rt<<1]);
summax[rt]=max(summax[rt<<1|1],summax[rt<<1]);
}
void updatemax(int pos,int c,int l,int r,int rt)
{
if(l==r)
{
summax[rt]=c;
return;
}
int m=(l+r)>>1;
if(pos<=m)
updatemax(pos,c,l,m,rt<<1);
if(m<pos)
updatemax(pos,c,m+1,r,rt<<1|1);
summax[rt]=max(summax[rt<<1],summax[rt<<1|1]);
}
void updatemin(int pos,int c,int l,int r,int rt)
{
if(l==r)
{
summin[rt]=c;
return;
}
int m=(l+r)>>1;
if(pos<=m)
updatemin(pos,c,l,m,rt<<1);
if(m<pos)
updatemin(pos,c,m+1,r,rt<<1|1);
summin[rt]=min(summin[rt<<1],summin[rt<<1|1]);
}
int querymax(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return summax[rt];
int m=(l+r)>>1;
int ans=0;
if(L<=m)
ans=max(ans,querymax(L,R,l,m,rt<<1));
if(m<R)
ans=max(ans,querymax(L,R,m+1,r,rt<<1|1));
return ans;
}
int querymin(int L,int R,int l,int r,int rt)
{
if(L<=l&&r<=R)
return summin[rt];
int m=(l+r)>>1;
int ans=INF;
if(L<=m)
ans=min(ans,querymin(L,R,l,m,rt<<1));
if(m<R)
ans=min(ans,querymin(L,R,m+1,r,rt<<1|1));
return ans;
}
int main()
{
while(~scanf("%d %d",&n,&m))
{
tot=0;
memset(rebuild,0,sizeof(rebuild));
memset(summin,0,sizeof(summin));
memset(summax,0,sizeof(summax));
build(1,n,1);
while(m--)
{
getchar();
char c;
scanf("%c",&c);
if(c=='D')
{
int num;
scanf("%d",&num);
updatemax(num,num,1,n,1);
updatemin(num,num,1,n,1);
rebuild[++tot]=num;
}
else if(c=='R')
{
int u=rebuild[tot--];
updatemax(u,0,1,n,1);
updatemin(u,n+1,1,n,1);
}
else if(c=='Q')
{
int num;
scanf("%d",&num);
int maxx=querymax(1,num,1,n,1);
int minn=querymin(num,n,1,n,1);
if(maxx==minn)
printf("0\n");
else
printf("%d\n",minn-maxx-1);
}
}
}
}
2.线段树维护连续子区间 先将村庄抽象成一个点 正常下值为1 被摧毁后变成0 即可将题目抽象成一个求目标节点所在连续1序列的区间的长度 所以可以维护两个数组
一个是从左边开始累加区间长度的数组(lsum[]),一个是从右边开始累加区间长度的数组(rsum[])
(代码有bug)
/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int lsum[maxn<<2],rsum[maxn<<2],flag;
int tot,rebuild[maxn<<2];
void build(int l,int r,int rt)
{
if(l==r)
{
lsum[rt]=rsum[rt]=1;
return;
}
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
lsum[rt]=lsum[rt<<1]+lsum[rt<<1|1];
rsum[rt]=rsum[rt<<1]+rsum[rt<<1|1];
}
void PushUp(int rt,int m)
{
lsum[rt]=lsum[rt<<1];
rsum[rt]=rsum[rt<<1|1];
if(lsum[rt]==m-(m>>1))
lsum[rt]+=lsum[rt<<1|1];
if(rsum[rt]==(m>>1))
rsum[rt]+=rsum[rt<<1];
}
void update(int pos,int c,int l,int r,int rt)
{
if(l==r)
{
lsum[rt]=rsum[rt]=c;
return;
}
int m=(l+r)>>1;
if(pos<=m)
update(pos,c,l,m,rt<<1);
if(m<pos)
update(pos,c,m+1,r,rt<<1|1);
PushUp(rt,r-l+1);
}
int query(int pos,int l,int r,int rt)
{
if(rsum[rt]>=r-pos+1)
{
flag=1;
return rsum[rt];
}
if(lsum[rt]>=pos-l+1)
{
flag=1;
return lsum[rt];
}
if(l==r)
return 0;
int m=(l+r)>>1;
int ans;
if(pos<=m)
{
ans=query(pos,l,m,rt<<1);
if(flag)
flag=0,ans+=lsum[rt<<1|1];
}
if(m<pos)
{
ans=query(pos,m+1,r,rt<<1|1);
if(flag)
flag=0,ans+=rsum[rt<<1];
}
return ans;
}
int main()
{
int n,m;
while(~scanf("%d %d",&n,&m))
{
memset(lsum,0,sizeof(lsum));
memset(rsum,0,sizeof(rsum));
memset(rebuild,0,sizeof(rebuild));
build(1,n,1);
tot=0,flag=0;
while(m--)
{
getchar();
char c;
scanf("%c",&c);
if(c=='D')
{
int num;
scanf("%d",&num);
update(num,0,1,n,1);
rebuild[++tot]=num;
}
if(c=='R')
{
int u=rebuild[tot--];
update(u,1,1,n,1);
}
if(c=='Q')
{
int num;
scanf("%d",&num);
printf("%d\n",query(num,1,n,1));
}
}
}
}
Assign the task
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3974
模板题 但是建树过程不一样 最近才搞懂了为什么要先dfs先序 因为要用线段树存储数据 就必须处理成链式的数据 所以首先要把树形的数据结构转换成链式 然后找入度为0的点作为根节点进行dfs序 然后套线段树模板即可
/**Today you do things people will not do,
tomorrow you will do things people can not do.**/
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstring>
#define ll long long
#define lson l,m,cnt<<1
#define rson m+1,r,cnt<<1|1
//priority_queue <int,vector<int>,greater<int> > Q;//优先队列递增
//priority_queue<int>Q;//递减
using namespace std;
const int maxn = 5e4+10;
const int mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double PI = acos(-1.0);
const double eps = 1e-6;
int s[maxn],e[maxn];
int sum[maxn<<2];
vector<int> G[maxn];
int vis[maxn];
int tot;
void dfs(int rt)
{
s[rt]=++tot;
for(int i=0; i<G[rt].size(); i++)
dfs(G[rt][i]);
e[rt]=tot;
}
void build(int l,int r,int rt)
{
sum[rt]=-1;
if(l==r)
return;
int m=(l+r)>>1;
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
}
void update(int L,int R,int c,int l,int r,int rt)
{
if(l==r)
{
sum[rt]=c;
return;
}
int m=(l+r)>>1;
if(L<=m)
update(L,R,c,l,m,rt<<1);
if(m<R)
update(L,R,c,m+1,r,rt<<1|1);
}
int query(int pos,int l,int r,int rt)
{
if(l==r)
return sum[rt];
int m=(l+r)>>1;
if(pos<=m)
return query(pos,l,m,rt<<1);
if(m<pos)
return query(pos,m+1,r,rt<<1|1);
}
int main()
{
int t,k=1;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
int n;
scanf("%d",&n);
for(int i=1; i<=n; i++)
G[i].clear();
for(int i=1; i<n; i++)
{
int u,v;
scanf("%d %d",&u,&v);
G[v].push_back(u);
vis[u]=1; //标记u为有入度的点
}
tot=0;
for(int i=1; i<=n; i++)
{
if(vis[i]==0) //入度为0的点为树根
{
dfs(i);
break;
}
}
build(1,n,1);
int m;
scanf("%d",&m);
printf("Case #%d:\n",k++);
for(int i=1; i<=m; i++)
{
char c[10];
cin>>c;
if(c[0]=='C')
{
int x;
scanf("%d",&x);
printf("%d\n",query(s[x],1,n,1));
}
if(c[0]=='T')
{
int x,y;
scanf("%d %d",&x,&y);
update(s[x],e[x],y,1,n,1);
}
}
}
return 0;
}
Vases and Flowers
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4614
还没搞懂 先占个坑 以后搞懂了填坑
线段树还没搞懂的知识点:
1.区间合并
2.扫描线

浙公网安备 33010602011771号