HDU6703
题意就不说了,直接分析吧。
对于1操作,a[pos]+1e7,但是我们每次询问是(1,n)范围内的,这个操作显然就是把这个点给删了。
对于2操作,询问[1,r]区间内>=k且不等于a[i] (1<=i<=r) 的最小的数。
题目做法如下:
对位置建权值线段树,每个结点表示一个区间,每个结点维护一个区间的位置的最大值。
(这里有些绕脑,如果看不懂的话需要画图便于理解。)
我们直接看询问,格式是(R,k),线段树是(l,r,rt,k,R),mx[rt]表示rt这个区间的最大位置
如果某一个区间mx[rt]<=R,那么就说明rt里面的每一个数都在[1,R]内,这个显然不符合条件。
将这个剪枝加入query,就可以过这个题目了
#include <bits/stdc++.h>
#define sc(n) scanf("%d",&n)
#define pt(n) printf("%d\n",n)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define vi vector<int>
#define vl vector<long long>
#define pb push_back
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5+7;
int n,m;
int a[maxn],aid[maxn],mx[maxn<<2];
void pushup(int rt)
{
mx[rt] = max(mx[rt<<1],mx[rt<<1|1]);
}
void build(int l,int r,int rt)
{
if(l==r)
{
mx[rt] = aid[l];
return;
}
int mid = (l+r)/2;
build(l,mid,rt<<1);
build(mid+1,r,rt<<1|1);
pushup(rt);
}
void update(int l,int r,int rt,int x)
{
if(l==r)
{
mx[rt] = aid[l];
return;
}
int mid = (l+r)/2;
if(x<=mid) update(l,mid,rt<<1,x);
else update(mid+1,r,rt<<1|1,x);
pushup(rt);
}
int query(int l,int r,int rt,int R,int k)
{
if(r<k) return 0;
if(mx[rt]<=R) return 0;
if(l==r) return l;
int mid = (l+r)/2;
int ans;
if(mx[rt<<1]>R) ans = query(l,mid,rt<<1,R,k);
if(ans) return ans;
return query(mid+1,r,rt<<1|1,R,k);
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",a+i);
aid[a[i]] = i;
}
aid[n+1] = n+1;
build(1,n+1,1);
int lans = 0;
for(int i=0;i<m;i++)
{
int op,t1,t2;
scanf("%d",&op);
if(op==1)
{
scanf("%d",&t1);
t1^=lans;
aid[a[t1]] = n+1;
update(1,n+1,1,a[t1]);
}
else
{
//ans = INF;
scanf("%d%d",&t1,&t2);
t1^=lans,t2^=lans;
lans = query(1,n+1,1,t1,t2);
//lans = ans;
printf("%d\n",lans);
}
}
}
return 0;
}
(一句话总结:比赛时这个题目思路不清晰,权值线段树怎么维护一直没想明白,而且死扣这个题,浪费了太多时间。)
HDU6705
题意很简单,不赘述。
先把每个点的出度按权值从小到大排个序。
把询问离线。
然后先把所有的边加进优先队列(起点,第几条边,权值)。
每次扩展可以从当前点取下一条边,和从当前的点取下一个最小的出边。
(这里说的不是很清楚,具体看代码第61到63行)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+7;
struct edge
{
int to;
ll w;
bool operator<(const edge &rhs)const
{
return w<rhs.w;
}
};
vector<edge> G[maxn];
struct node
{
int s,id;
ll w;
bool operator<(const node &rhs)const
{
return w>rhs.w;
}
};
int n,m,k,qy[maxn];
ll ans[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
memset(G,0,sizeof(G));
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
{
int x,y;
ll z;
scanf("%d%d%lld",&x,&y,&z);
G[x].push_back({y,z});
}
int mx = 0;
for(int i=1;i<=k;i++)
{
scanf("%d",qy+i);
mx = max(mx,qy[i]);
}
for(int i=1;i<=n;i++) sort(G[i].begin(),G[i].end());
priority_queue<node> q;
for(int i=1;i<=n;i++)
{
if(G[i].size()) q.push({i,0,G[i][0].w});
}
int tot = 0;
while(true)
{
ll w = q.top().w;
int s = q.top().s,id = q.top().id;
q.pop();
ans[++tot] = w;
if(tot==mx) break;
if(id+1<(int)G[s].size()) q.push({s,id+1,w-G[s][id].w+G[s][id+1].w});
int v = G[s][id].to;
if(G[v].size()) q.push({v,0,w+G[v][0].w});
}
for(int i=1;i<=k;i++) printf("%lld\n",ans[qy[i]]);
}
return 0;
}
(一句话总结:比赛时看到第k大就往可持久化线段树上想,导致根本无从下手,其实只要离线了,用优先队列可以很简单的维护,而且在CF上做过不少这种题目,思路竟然还是不到位,实在不应该)