NOIP模拟22「d·e·f」
T1:d
枚举。
现在都不敢随便打枚举了。
实际上我们只关注最后留下的矩阵中最小的长与宽即可。
所以我们将所有矩阵按a的降序排列。
从第\(n-m\)个开始枚举。
因为你最多拿走\(m\)个。
考虑到交面积是越交越小的,所以我们尽可能的多拿矩阵走。
我们将前\(n-m\)个矩阵丢进堆里,按b排序,小根堆。
我们只关心b第\(n-m\)大的矩阵,因为我们已经决定要刘m个了,原因已经说过了。
所以我们要让堆顶尽可能大。并维护堆的大小为\(n-m\)即可。具体实现见代码。
1A代码。。我爱战神。。。。
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
#define inf INT_MAX
const int N=1e5+4;
int n,m,t;
struct mat
{
ll a,b;
bool operator<(const mat b_) const
{
return a>b_.a;
}
mat(){}
mat(int a_,int b_){a=a_,b=b_;}
}x[N];
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
class Heap
{
private:
int tot;
mat heap[N<<2];
void push_up(int x)
{
while(x>1)
{
if(heap[x].b<heap[x>>1].b)
{
swap(heap[x],heap[x>>1]);
x>>=1;
}
else break;
}
}
void push_down(int x)
{
int s=x<<1;
while(s<=tot)
{
if(s<tot&&heap[s].b>heap[s+1].b) s++;
if(heap[s].b<heap[x].b)
{
swap(heap[x],heap[s]);
x=s,s=x<<1;
}
else break;
}
}
public:
Heap(){tot=0;}
void reset()
{
for(rr int i=1;i<=tot;i++)
heap[i]=mat(0,0);
tot=0;
}
void insert(mat x)
{
heap[++tot]=x;
push_up(tot);
}
mat top(){return heap[1];}
void pop()
{
heap[1]=heap[tot--];
push_down(1);
}
void remove(int x)
{
heap[x]=heap[tot--];
push_up(x);
push_down(x);
}
int size(){return tot;}
}heap;
};
using namespace STD;
int main()
{
t=read();
while(t--)
{
n=read(),m=read();
for(rr int i=1;i<=n;i++)
x[i].a=read(),x[i].b=read();
sort(x+1,x+1+n);
for(rr int i=1;i<=n-m;i++)
heap.insert(x[i]);
ll ans=-inf;
ans=max(ans,heap.top().b*x[n-m].a);
for(rr int i=n-m+1;i<=n;i++)
{
if(x[i].b>heap.top().b)
{
heap.pop();
heap.insert(x[i]);
}
ans=max(ans,x[i].a*heap.top().b);
}
printf("%lld\n",ans);
heap.reset();
}
}
战神跟我讲的只关心最后留谁这个思想我考试想到了。但当时只是骗分,用这个思想打了个状压就走了。没细想。
这道题写的骗分都对了,开心。QWQ
T2:e
考试时想出了这个联通块其实就是从\(LCA\)到各个点,打了个树剖就走了,暴力跳得父亲。
本机跑进了1s,用的90000多的数据,还挺开心,以为稳了。结果\(T\)了,笑死。
正解是还要打一个主席树,以节点为版本,记录从自己到根的权值情况。
AC代码:
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define ll long long
#define rr register
#define inf INT_MAX
const int N=1e5+6;
int n,q,type,cnt;
int a[N],x[N],a_[N];
int to[N<<1],dire[N<<1],head[N];
inline void add(int f,int t)
{
static int num1=0;
to[++num1]=t;
dire[num1]=head[f];
head[f]=num1;
}
int read()
{
rr int x_read=0,y_read=1;
rr char c_read=getchar();
while(c_read<'0'||c_read>'9')
{
if(c_read=='-') y_read=-1;
c_read=getchar();
}
while(c_read<='9'&&c_read>='0')
{
x_read=(x_read*10)+(c_read^48);
c_read=getchar();
}
return x_read*y_read;
}
int fa[N],son[N],top[N],size[N],depth[N];
void dfs1(int x)
{
size[x]=1;
for(rr int i=head[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
fa[to[i]]=x;
depth[to[i]]=depth[x]+1;
dfs1(to[i]);
size[x]+=size[to[i]];
if(size[to[i]]>size[son[x]]) son[x]=to[i];
}
}
void dfs2(int x)
{
if(x==son[fa[x]]) top[x]=top[fa[x]];
else top[x]=x;
for(rr int i=head[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
dfs2(to[i]);
}
}
int LCA(int x,int y)
{
while(top[x]!=top[y])
{
if(depth[top[x]]>depth[top[y]])
x=fa[top[x]];
else y=fa[top[y]];
}
return depth[x]<depth[y]?x:y;
}
class Pst
{
private:
int tot;
int root[N];
int lc[(N<<1)+N*20];
int rc[(N<<1)+N*20];
int sum[(N<<1)+N*20];
void Insert(int before,int &now,int l,int r,int val);
void Build(int &now,int l,int r);
int query_sum(int before,int now,int l,int r,int st,int en);
int query_rank(int before,int now,int l,int r,int rank);
public:
void build(){Build(root[0],1,n+2);}
void insert(int fa,int son,int val){Insert(root[fa],root[son],1,n+2,val);}
int prev(int fa,int son,int val)
{
int rank=query_sum(root[fa],root[son],1,n+2,1,val);
return query_rank(root[fa],root[son],1,n+2,rank);
}
int succ(int fa,int son,int val)
{
int rank=query_sum(root[fa],root[son],1,n+2,1,val);
return query_rank(root[fa],root[son],1,n+2,rank+1);
}
}t;
void Pst::Build(int &now,int l,int r)
{
now=++tot;
if(l==r) return;
int mid=(l+r)>>1;
Build(lc[now],l,mid),Build(rc[now],mid+1,r);
}
void Pst::Insert(int before,int &now,int l,int r,int val)
{
now=++tot;
sum[now]=sum[before];
if(l==r){sum[now]++;return;}
int mid=(l+r)>>1;
if(val<=mid)
{
rc[now]=rc[before];
Insert(lc[before],lc[now],l,mid,val);
}
else
{
lc[now]=lc[before];
Insert(rc[before],rc[now],mid+1,r,val);
}
sum[now]=sum[lc[now]]+sum[rc[now]];
}
int Pst::query_sum(int before,int now,int l,int r,int st,int en)
{
if(st<=l&&r<=en) return sum[now]-sum[before];
int mid=(l+r)>>1;
int ret=0;
if(st<=mid) ret+=query_sum(lc[before],lc[now],l,mid,st,en);
if(mid<en) ret+=query_sum(rc[before],rc[now],mid+1,r,st,en);
return ret;
}
int Pst::query_rank(int before,int now,int l,int r,int rank)
{
if(l==r)
{
if(sum[now]-sum[before])
return l;
return inf;
}
int mid=(l+r)>>1;
int num=(sum[lc[now]]-sum[lc[before]]);
if(num>=rank)
return query_rank(lc[before],lc[now],l,mid,rank);
return query_rank(rc[before],rc[now],mid+1,r,rank-num);
}
void dfs3(int x)
{
t.insert(fa[x],x,a[x]);
for(rr int i=head[x];i;i=dire[i])
{
if(to[i]==fa[x]) continue;
dfs3(to[i]);
}
}
int find(int x)
{
int l=1,r=cnt;
while(l<r)
{
int mid=(l+r+1)>>1;
if(a_[mid]<=x) l=mid;
else r=mid-1;
}
return l;
}
};
using namespace STD;
int main()
{
n=read(),q=read(),type=read();
for(rr int i=1;i<=n;i++) a[i]=a_[i]=read();
for(rr int i=1;i<n;i++)
{
int u=read(),v=read();
add(u,v),add(v,u);
}
sort(a_+1,a_+1+n);
cnt=unique(a_+1,a_+1+n)-a_-1;
for(rr int i=1;i<=n;i++)
a[i]=lower_bound(a_+1,a_+1+cnt,a[i])-a_;
t.build();
dfs1(1);
dfs2(1);
dfs3(1);
int lastans=0;
while(q--)
{
int r=read(),k=read();
for(rr int i=1;i<=k;i++)
{
x[i]=read();
x[i]=(x[i]-1+lastans*type)%n+1;
}
int lca=x[1];
int r_=find(r);
for(rr int i=2;i<=k;i++)
lca=LCA(lca,x[i]);
int ans=inf;
for(rr int i=1;i<=k;i++)
{
int prev=t.prev(fa[lca],x[i],r_);
int succ=t.succ(fa[lca],x[i],r_);
if(prev!=inf)
ans=min(ans,abs(a_[prev]-r));
if(succ!=inf)
ans=min(ans,abs(a_[succ]-r));
}
lastans=ans;
printf("%d\n",ans);
for(rr int i=1;i<=k;i++)
x[i]=0;
}
}
这道题还涉及到了主席树加减,我直接懵逼了。。。。。。
跑回去学了一下,A了道板子题。
改题时二分写错了,改了半天,这道题细节蛮多的,下面我会提到我改题时碰到的几个细节。
1.二分,我原本用的lower_bound,STL库里的。他的返回值是“在保证有序性的情况下这个数据应该被插在哪”
我为什么会提到这个呢??因为我一开始给r离散化,用的这个,但他的返回是大于或等于r的值得位置,我主席树里是要用小于或等于r的值的位置。所以WA了
后来改为手写,用的李煜东的那本书(算法竞赛进阶指南)上给的算法,才过的。
要看着本书的要注意,打二分一定严格按照标程,他给出的两种二分写法是严格与求前趋后继向配套的,是不可混用的我混用了,然后调了半天FWF。。。
2.联通块是包括lca的,所以在查前趋后继时要用fa[lca]与x[i]的版本,而不是lca与x[i]的版本。
3.主席树查前趋后继的时候要注意处理没有前趋后继的情况。
大概就这些吧。
T3:f
还在调,先鸽掉。