HDU2018多校第三场部分题目

HDU2018多校第三场部分题目

H Monster Hunter(hdu 6326)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6326

题解
如果没有树的限制的话,对于ai<=bi的点,按照ai升序先后攻击肯定最优,对于ai>bi的点,按照bi降序先后攻击肯定最优。
之前写过一道类似的,那个题是括号序列。
我们先把这些点放到一个堆里面。对于一个点,如果它当前是最优的,并且它的父亲已经被攻击了,那么它就可以直接被攻击。
如果它的父亲没有被攻击的话,那么当它的父亲被攻击后它会立即被攻击,我们把它和它的父亲合并一下再扔到堆里就好了。

代码

#include
#define ll long long
#define N 100010
using namespace std;
int T,n,flag[N],fa[N],pa[N],k,la[N],ff[N*2];
ll A[N],B[N],ans,now;
struct node{int a,b;}e[N*2];
struct info{
  int x;ll a,b,y;
  bool operator<(const info &p)const{return y>p.y;}
};
priority_queueq1,q2;

void add(int a,int b)
{
  e[++k]=(node){a,b};ff[k]=la[a];la[a]=k;
  e[++k]=(node){b,a};ff[k]=la[b];la[b]=k;
}

void dfs(int x,int pre)
{
  fa[x]=pre;
  for(int a=la[x];a;a=ff[a])
    if(e[a].b!=pre)dfs(e[a].b,x);
}

int find(int x)
{
  if(pa[x]==x)return x;
  return pa[x]=find(pa[x]);
}

int main()
{
  int x,y;ll a,b;
  scanf("%d",&T);
  while(T--)
  {
    memset(fa,0,sizeof(fa));
    memset(ff,0,sizeof(ff));
    memset(la,0,sizeof(la));
    memset(flag,0,sizeof(flag));
    scanf("%d",&n);ans=0;now=0;k=0;
    for(int i=2;i<=n;i++)
    {
      scanf("%I64d%I64d",&a,&b);A[i]=a;B[i]=b;
      if(aelse q2.push((info){i,a,b,-b});
    }
    for(int i=1;iscanf("%d%d",&x,&y),add(x,y);
    dfs(1,0);flag[1]=1;
    for(int i=1;i<=n;i++)pa[i]=i;
    while(!q1.empty())
    {
      info tmp=q1.top();q1.pop();x=find(tmp.x);
      if(tmp.a!=A[x]||tmp.b!=B[x])continue;
      y=find(fa[x]);
      if(flag[y])ans=min(ans,now-A[x]),now+=B[x]-A[x],flag[x]=1;
      else
      {
        a=max(A[y]-B[y]+A[x],A[y]);
        b=B[x]-A[x]+B[y]-A[y]+a;
        A[y]=a;B[y]=b;pa[x]=y;
        if(aelse q2.push((info){y,a,b,-b});
      }
    }
    while(!q2.empty())
    {
      info tmp=q2.top();q2.pop();x=find(tmp.x);
      if(tmp.a!=A[x]||tmp.b!=B[x])continue;
      y=find(fa[x]);
      if(flag[y])ans=min(ans,now-A[x]),now+=B[x]-A[x],flag[x]=1;
      else
      {
        a=max(A[y]-B[y]+A[x],A[y]);
        b=B[x]-A[x]+B[y]-A[y]+a;
        A[y]=a;B[y]=b;pa[x]=y;
        q2.push((info){y,a,b,-b});
      }
    }
    printf("%I64d\n",-ans);
  }
  return 0;
}

J Rectangle Radar Scanner(hdu 6328)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6328

题解
感觉这个题可以线段树套线段树的,5秒应该能过,空间也很充裕,隔壁队好像被卡了,就懒得写了。
写了题解做法。
这个题模数不是指数,不能求逆元,不能差分。
对于x坐标分治,对于每层分治,找到中点,把不跨越中点的矩形分治到两边。
对于跨越中点的区间,从中点切一刀分成两半分别算。
这样对于两半都少了一维限制,也就不存在差分的问题了。

代码

#include
#define inf 2100000000
#define ll long long
#define N 100010
#define M 1000010
using namespace std;
int Tx,n,m,mod,r,k,a[M],b[M],c[M],d[M],y[N],w[N];
int ans[M],minn[M],maxn[M];ll res,p,q;
struct info{int xl,xr,yl,yr,id;}t[M],s[M];
struct node{int x,l,r,id;}A[M],B[M];
struct data{int minn,maxn;ll mul;};
bool cmp1(const node &p,const node &q){return p.x>q.x;}
bool cmp2(const node &p,const node &q){return p.x<q.x;}

class seg_tree
{
  data merge(data A,data B)
  {
    data res;
    res.mul=A.mul*B.mul%k;
    res.maxn=max(A.maxn,B.maxn);
    res.minn=min(A.minn,B.minn);
    return res;
  }
  public:
  data t[N*4];
  void build(int x,int l,int r)
  {
    t[x]=(data){inf,0,1};
    if(l==r)return;
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    build(lc,l,mid);build(rc,mid+1,r);
  }
  void insert(int x,int l,int r,int pos,int val)
  {
    t[x].mul=t[x].mul*val%k;
    t[x].maxn=max(t[x].maxn,val);
    t[x].minn=min(t[x].minn,val);
    if(l==r)return;
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    if(pos<=mid)insert(lc,l,mid,pos,val);
    else insert(rc,mid+1,r,pos,val);
  }
  data qry(int x,int l,int r,int ql,int qr)
  {
    if(ql<=l&&r<=qr)return t[x];
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    if(qr<=mid)return qry(lc,l,mid,ql,qr);
    if(ql>mid)return qry(rc,mid+1,r,ql,qr);
    return merge(qry(lc,l,mid,ql,qr),qry(rc,mid+1,r,ql,qr));
  }
  void clear(int x,int l,int r,int pos)
  {
    t[x]=(data){inf,0,1};
    if(l==r)return;
    int mid=l+r>>1,lc=x<<1,rc=lc+1;
    if(pos<=mid)clear(lc,l,mid,pos);
    else clear(rc,mid+1,r,pos);
  }
}T;

void solve(int l,int r,int L,int R)
{
  if(l>r)return;
  int mid=L+R>>1,lx=l,rx=r,t1=0,t2=0;
  for(int i=l;i<=r;i++)
  {
    if(t[i].xrs[lx++]=t[i];continue;}
    if(t[i].xl>mid){s[rx--]=t[i];continue;}
    A[++t1]=(node){t[i].xl,t[i].yl,t[i].yr,t[i].id};
    B[++t2]=(node){t[i].xr,t[i].yl,t[i].yr,t[i].id};
  }
  // 
  sort(A+1,A+t1+1,cmp1);
  for(int i=1,j=mid;i<=t1;i++)
  {
    for(;j>=L&&A[i].x<=j;j--)T.insert(1,1,n,y[j],w[j]);
    data tmp=T.qry(1,1,n,A[i].l,A[i].r);
    ans[A[i].id]=ans[A[i].id]*tmp.mul%k;
    maxn[A[i].id]=max(maxn[A[i].id],tmp.maxn);
    minn[A[i].id]=min(minn[A[i].id],tmp.minn);
  }
  for(int i=L;i<=mid;i++)T.clear(1,1,n,y[i]);
  sort(B+1,B+t2+1,cmp2);
  for(int i=1,j=mid+1;i<=t2;i++)
  {
    for(;j<=R&&B[i].x>=j;j++)T.insert(1,1,n,y[j],w[j]);
    data tmp=T.qry(1,1,n,B[i].l,B[i].r);
    ans[B[i].id]=ans[B[i].id]*tmp.mul%k;
    maxn[B[i].id]=max(maxn[B[i].id],tmp.maxn);
    minn[B[i].id]=min(minn[B[i].id],tmp.minn);
  }
  for(int i=mid+1;i<=R;i++)T.clear(1,1,n,y[i]);
  //
  for(int i=l;i<=r;i++)t[i]=s[i];
  solve(l,lx-1,L,mid-1);solve(rx+1,r,mid+1,R);
}

int main()
{
  int xl,xr,yl,yr;
  scanf("%d",&Tx);
  while(Tx--)
  {
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d%d",&y[i],&w[i]);
    scanf("%d%d%d%d%d",&m,&a[0],&b[0],&c[0],&d[0]);
    scanf("%lld%lld%d%d%d",&p,&q,&r,&mod,&k);
    for(int i=1;i<=m;i++)
    {
      a[i]=(p*a[i-1]+q*b[i-1]+r)%mod;
      b[i]=(p*b[i-1]+q*a[i-1]+r)%mod;
      c[i]=(p*c[i-1]+q*d[i-1]+r)%mod;
      d[i]=(p*d[i-1]+q*c[i-1]+r)%mod;
      xl=min(a[i]%n,b[i]%n)+1;
      xr=max(a[i]%n,b[i]%n)+1;
      yl=min(c[i]%n,d[i]%n)+1;
      yr=max(c[i]%n,d[i]%n)+1;
      t[i]=(info){xl,xr,yl,yr,i};
      ans[i]=1;maxn[i]=0;minn[i]=inf;
    }
    T.build(1,1,n);solve(1,m,1,n);res=0;
    for(int i=1;i<=m;i++)
    {
      if(!maxn[i])continue;
      res+=(ans[i]^maxn[i]^minn[i]);
    }
    printf("%lld\n",res);
  }
  return 0;
} 

K Transport Construction(hdu 6329)

题目描述
http://acm.hdu.edu.cn/showproblem.php?pid=6329

题解
考虑一种奇怪的求mst的办法,对于每个点求出离它最近的点,将其两两连边。
n个点n条边最后肯定是一些环和一些树。
我们把连通的点合并起来,然后再在缩好点的图上对于每个点再求一下不再同一个联通快的最近边,再跑合并一下。
每次合并,假设有n个联通快,合并之后最多只会剩下n/2个联通快。
所以最多只要log次合并就可以完成mst的构建。
至于怎么找最近点,因为题目规定的距离是点积,其实就只有下凸壳上的点会成为答案。
所以我们按节点联通快的颜色分治,左边区间的点和右边的凸包合并,右边区间的点和左边的凸包合并。、
这样复杂度就是O(nlogn^2)。

代码

#include
#define ll long long
#define inf 999999999999999ll
#define N 100010
using namespace std;
int T,n,m,cnt,X[N],Y[N],w[N],fa[N],pos[N],h[N];
ll ans,f[N];
struct node{
  ll x,y,p;
  ll operator*(const node &h)const{return x*h.x+y*h.y;}
  bool operator<(const node &h)const{return preturn x*h.y-y*h.x;}
  node operator-(const node &h)const{return (node){x-h.x,y-h.y};}
}t[N],s[N];
vectorq[N*4];
bool cmp1(const node &a,const node &b){return a.xx||(a.x==b.x&&a.yy);}
bool cmp2(const node &a,const node &b){return a.y*b.xy*a.x;}

void insert(int now,const node &x)
{
  int top=q[now].size()-1;
  while(top>0&&(q[now][top]-q[now][top-1])+(x-q[now][top])<=0)q[now].pop_back(),top--;
  q[now].push_back(x);
}

int solve(int l,int r,int L,int R)
{
  int now=++cnt;
  if(L==R)
  {
    sort(t+l,t+r+1,cmp1);
    for(int i=l;i<=r;i++)insert(now,t[i]);
    sort(t+l,t+r+1,cmp2);

    return now;
  }
  int mid=L+R>>1,po;
  for(int i=l;i<=r;i++)
    if(t[i].p>mid){po=i-1;break;}
  int lc=solve(l,po,L,mid),rc=solve(po+1,r,mid+1,R);

  for(int i=l,j=0;i<=po;i++)
  {
    while(j<q[rc].size()-1&&q[rc][j]*t[i]>=q[rc][j+1]*t[i])j++;
    if(q[rc][j]*t[i]q[rc][j]*t[i],pos[t[i].p]=q[rc][j].p;
  }
  for(int i=po+1,j=0;i<=r;i++)
  {
    while(j<q[lc].size()-1&&q[lc][j]*t[i]>=q[lc][j+1]*t[i])j++;
    if(q[lc][j]*t[i]q[lc][j]*t[i],pos[t[i].p]=q[lc][j].p;
  }

  int x=0,y=0;
  for(;x<q[lc].size()&&y<q[rc].size();)
  {
    if(q[lc][x].x<q[rc][y].x)insert(now,q[lc][x++]);
    else insert(now,q[rc][y++]);
  }
  while(x<q[lc].size())insert(now,q[lc][x++]);
  while(y<q[rc].size())insert(now,q[rc][y++]);
  int tot=l;
  for(x=l,y=po+1;x<=po&&y<=r;)
  {
    if(t[x].y*t[y].xy].y*t[x].x)s[tot++]=t[x++];
    else s[tot++]=t[y++];
  }
  while(x<=po)s[tot++]=t[x++];
  while(y<=r)s[tot++]=t[y++];
  for(int i=l;i<=r;i++)t[i]=s[i];
  return now;
}

int find(int x)
{
  if(fa[x]==x)return x;
  return fa[x]=find(fa[x]);
}

int main()
{
  int x,y;
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d",&n);ans=0;
    for(int i=1;i<=n;i++)scanf("%d%d",&X[i],&Y[i]);
    for(int i=1;i<=n;i++)fa[i]=i,w[i]=0;
    while(1)
    {
      m=0;cnt=0;
      for(int i=1;i<=n;i++)
      {
        x=find(i);if(!w[x])h[w[x]=++m]=x;
        t[i]=(node){X[i],Y[i],w[x]};
      }
      if(m==1)break;
      for(int i=1;i<=m;i++)f[i]=inf;
      sort(t+1,t+n+1);solve(1,n,1,m);
      for(int i=1;i<=m;i++)
      {
        x=find(h[i]);y=find(h[pos[i]]);
        if(x!=y)ans+=f[i],fa[y]=x;
      }
      for(int i=1;i<=cnt;i++)q[i].clear();
      for(int i=1;i<=n;i++)w[i]=0;
    }
    printf("%lld\n",ans);
  }
  return 0;
}

你可能感兴趣的:(题解,——分治,数据结构,——最小生成树,——凸包,贪心,——堆)