Array:HDU 6703
http://acm.hdu.edu.cn/showproblem.php?pid=6703
题意:给出有n个数的序列,每个值都不同,取值范围在1-n之间。
有m个操作:(1,pos)序列a[pos]+10000000;(2,r,k)询问最小的值满足 不等于下标1—r之间的任何值 并且不小于k。
思路:因为序列是包含1—n的每个值,修改询问也是与值的大小有关,所以想到建权值线段树。
询问中需要满足下标大于r,因此要记录下标值。大于r,尽量取大的,所以维护下标的最大值。
询问中不少于k,答案大于等于k,r的取值1—n,当r等于n,序列中存的是n个数,答案为n+1,并且k的最大取值也为n,所以询问结果 在k—n+1之间,需要建一颗1—n+1的权值线段树。
对于操作一:某个值加上很大的数,操作二询问的是最小值,那么修改完的值一定不会作为答案。但修改之前的值可以作为答案出现,它不同于现序列中的任何值。每次询问都可能取该值,所以将该值下标改为无穷大,保证每次能被询问到。
对于操作二:要找到一个值大于等于k,下标大于r。所以询问k—n+1范围内第一个下标大于r的是多少。尽量往左子树找,如果找不到就往右子树找,进入左子树的时候判断值及下标最大值是否满足要求。
因为每次pos,r,k由上次结果异或得到,所以必须在线,复杂度为O( m * log n )。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int n,m,ans,x,y,z,a[maxn],b[maxn];
struct node
{
int l,r,w;
}t[maxn<<2];
void build(int k,int l,int r)
{
t[k].l=l;t[k].r=r;
if(l==r){
t[k].w=b[l];
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
t[k].w=max(t[k<<1].w,t[k<<1|1].w);
}
void add(int k,int x)
{
if(t[k].l==t[k].r){
t[k].w+=10000000;
return ;
}
int mid=(t[k].l+t[k].r)>>1;
if(x<=mid) add(k<<1,x);
else add(k<<1|1,x);
t[k].w=max(t[k<<1].w,t[k<<1|1].w);
}
int ask(int k,int r,int v)
{
if(t[k].l==t[k].r){
if(t[k].l>=v&&t[k].w>r){
return t[k].l;
}
else return -1;
}
int mid=(t[k].l+t[k].r)>>1;
int ans=-1;
if(v<=mid&&t[k<<1].w>r){
ans=ask(k<<1,r,v);
}
if(ans==-1) ans=ask(k<<1|1,r,v);
return ans;
}
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]);
b[a[i]]=i;
}
b[n+1]=n+1;
build(1,1,n+1);
ans=0;
for(int i=1;i<=m;i++){
scanf("%d",&x);
if(x==1){
scanf("%d",&y);
y^=ans;
add(1,a[y]);
}
else{
scanf("%d%d",&y,&z);
y^=ans;z^=ans;
ans=ask(1,y,z);
printf("%d\n",ans);
}
}
}
return 0;
}
PATH:HDU 6705
http://acm.hdu.edu.cn/showproblem.php?pid=6705
题意:给出n个点,m条边,多次询问第k大的路径值。
思路:直接跑bfs,直到找到第k大的路径。但一个点可能有多条出边,菊花图,会有很多填到队列里面。所以,就要选更小的,想到了扩展每个点时,按照边的权值从小到大选择下一点。用vector记录相连的点和边权,并按边权从小到大排序。
然后呢,扩展到什么时候停啊,如何判断是否够询问的最大值mx了?
维护两个优先队列,一个q用于扩展下一个点,再一个qq记录当前得到的路径值。当qq的大小<询问最大值mx时,直接往q里面添加新路径,qq里面直接记录这种情况下的路径大小。当qq的大小==询问最大值mx时,表示已经够了询问最大值的数量,但还没有完全得到mx个最小的路径值。所以判断当前qq中的最大值与当前值大小,如果最大值>当前,把最大值删去,换成当前值,进入q;最大值<当前,舍弃当前值,不进入q,qq,退出循环。这样可以控制加入q的元素个数,后面进入的都是尽量小的路径值。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> p;
const ll maxn=5e4+5;
vector<p> v[maxn];
ll n,m,qr,a,b,c,k[maxn],ans[maxn];
struct cmp{
bool operator()(const p a,const p b)const{
return a.second>b.second;
}
};
bool cc(p a,p b)
{
return a.second<b.second;
}
priority_queue<ll> qq;
priority_queue<p,vector<p>,cmp> q;
void bfs(ll mx)
{
ll cnt=0;
for(ll i=0;i<mx;i++){
p f=q.top();
q.pop();
ll x=f.first,w=f.second;
ans[++cnt]=w;
if(cnt==mx) break;
ll len=v[x].size();
for(ll i=0;i<len;i++){
p ff=v[x][i];
ll xx=ff.first,ww=w+ff.second;
if(qq.size()==mx){
if(ww>qq.top()) break;
else{
qq.pop();
qq.push(ww);
q.push(p(xx,ww));
}
}
else{
qq.push(ww);
q.push(p(xx,ww));
}
}
}
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
while(!q.empty()) q.pop();
while(!qq.empty()) qq.pop();
scanf("%lld%lld%lld",&n,&m,&qr);
for(ll i=0;i<=n;i++) v[i].clear();
for(ll i=0;i<m;i++){
scanf("%lld%lld%lld",&a,&b,&c);
v[a].push_back(p(b,c));
q.push(p(b,c));
qq.push(c);
}
for(ll i=1;i<=n;i++){
sort(v[i].begin(),v[i].end(),cc);
}
ll kk=0;
for(ll i=0;i<qr;i++){
scanf("%lld",&k[i]);
kk=max(kk,k[i]);
}
bfs(kk);
for(ll i=0;i<qr;i++){
printf("%lld\n",ans[k[i]]);
}
}
return 0;
}
Fishing Master:HDU 6709
http://acm.hdu.edu.cn/showproblem.php?pid=6709
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll a[maxn],n,k;
priority_queue<ll> q;
int main()
{
int t;
scanf("%d",&t);
while(t--){
while(!q.empty()) q.pop();
scanf("%lld%lld",&n,&k);
ll ans=k,sum=0;
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
ans+=a[i];
sum+=a[i]/k;
q.push(a[i]%k);
}
if(sum>=n-1){
printf("%lld\n",ans);
continue;
}
else{
ll m=n-1-sum;
for(ll i=0;i<m;i++){
ll x=q.top();
q.pop();
ans+=(k-x);
}
}
printf("%lld\n",ans);
}
return 0;
}