HDU2018多校第六场部分题目

HDU2018多校第六场部分题目

这场训练的时候就过了四题,赛后看了题解感觉还是有很多可做题的。

C Ringland( HDU6364 )

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

题解
首先新郎和新娘的配对肯定不会有反向交叉,即3和1配对,2和4配对这样的情况。所以我们可以枚举断点然后计算答案。
统计答案显然就按顺时针的顺序贪心连线就好了。
当时想到这里然后就不会做了,并不知道改变断点之后怎么处理。
我们可以把新郎看成左括号,新娘看成有括号,从左往后左括号+1右括号-1。每个人的贡献分为两类,前缀和>0的新郎贡献为负,<=0的新郎贡献为正。前缀和>=0的新娘贡献为正,<0的新娘贡献为负。
每次移动一个位置等价于所有人的前缀和+1或-1,只会影响两种前缀和的贡献的正负性。
所以我们对于每种前缀和维护一下有多少个以及其正负性,然后每次右移更新一下就好了。

代码

#include 
#define ll long long
#define N 500010
#define M 2000010
using namespace std;
int T,n,L,a[N],b[N],tp[M],s[M],f[M],v[M],inv1[M],inv2[M];
ll res,A[M],B[M],ans;int sum,tot,val;
char ss[N],*h=ss+N,*t=h;
inline char getch()
{
  if(h==t)
  {
    if(t!=ss+N)return EOF;
    t=ss+fread(ss,1,N,stdin);
    h=ss;
  }
  return *h++;
}//卡读入丧心病狂 
inline int get()
{
  char ch;int v;
  while(!isdigit(ch=getch()));v=ch-48;
  while(isdigit(ch=getch()))v=v*10+ch-48;
  return v;
}
int main()
{
  T=get();
  while(T--)
  {
    n=get();L=get();res=(ll)n*L;sum=0;ans=0;
    for(int i=1;i<=n;i++)a[i]=get();
    for(int i=1;i<=n;i++)b[i]=get();
    for(int i=0;i<=n*2;i++)A[i]=B[i]=0,inv1[i]=inv2[i]=1;
    a[n+1]=L+1;b[n+1]=L+1;tot=0;
    for(int i=1,j=1;i<=n||j<=n;)
    {
      if(a[i]s[++tot]=a[i++],tp[tot]=1;
      else s[++tot]=b[j++],tp[tot]=-1;
    }
    for(int i=1;i<=tot;i++)s[tot+i]=L+s[i],tp[tot+i]=tp[i];
    for(int i=1;i<=tot;i++)
    {
      sum+=tp[i];f[i]=sum;
      if(tp[i]>0)v[i]=(sum>0?-1:1)*s[i];
      else v[i]=(sum<0?-1:1)*s[i];
      ans+=v[i];
      tp[i]>0?A[n+sum]+=v[i]:B[n+sum]+=v[i];
    }
    for(int i=1,j=tot+1,pos=0;i<=tot;i++,j++)
    {
      res=min(res,ans);
      if(tp[i]>0)A[n+f[i]]-=inv1[n+f[i]]*v[i],ans-=inv1[n+f[i]]*v[i];
      else B[n+f[i]]-=inv2[n+f[i]]*v[i],ans-=inv2[n+f[i]]*v[i];
      if(tp[i]>0)
      {
        ans-=A[n+pos+1]*2; ans-=B[n+pos]*2;
        A[n+pos+1]=-A[n+pos+1]; B[n+pos]=-B[n+pos];
        inv1[n+pos+1]=-inv1[n+pos+1]; inv2[n+pos]=-inv2[n+pos];
      }
      else
      {
        ans-=A[n+pos]*2;ans-=B[n+pos-1]*2;
        A[n+pos]=-A[n+pos];B[n+pos-1]=-B[n+pos-1];
        inv1[n+pos]=-inv1[n+pos]; inv2[n+pos-1]=-inv2[n+pos-1];
      }
      pos+=tp[i];sum+=tp[j]-tp[i];
      val=s[j];ans+=val;
      tp[j]>0?A[n+sum+pos]+=val:B[n+sum+pos]+=val;
    }
    printf("%I64d\n",res);
  }
  return 0;
}

D Shoot Game(HDU 6365)

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

题解
其实就是一个简单的区间dp。
将点极角排序,对于每个区间,只需要考虑区间中被完全覆盖的点就好了,对于剩下的点完全可以等到更大的区间再去考虑。当时一直没想通。。。
一个结论是,每次射击一定是射在2n个线段端点中的某一个。
对于一个区间,要想将该区间完全覆盖的线段都射光,就必须有一次射击要射到权值最大的那个线段。
所以预处理出每个区间完全覆盖的最大的线段,枚举在那条线段上射击的点,将另外两半的区间进行区间dp合并就好了。

代码

#include
#define ll long long
#define inf 999999999999999ll
#define N 605
using namespace std;
int T,n,tot,l[N],r[N],h[N],w[N],L[N],R[N];
ll f[N][N];
struct node{
  ll x,y;
  bool operator<(const node &p)const{
    return x*p.y-y*p.x<0;
  }
  bool operator==(const node &p)const{
    return x==p.x&&y==p.y;
  }
}t[N];
int main()
{
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d",&n);tot=0;
    memset(L,0,sizeof(L));
    memset(R,0,sizeof(R));
    for(int i=1;i<=n;i++)
    {
      scanf("%d%d%d%d",&h[i],&l[i],&r[i],&w[i]);
      t[++tot]=(node){l[i],h[i]};
      t[++tot]=(node){r[i],h[i]};
    }
    sort(t+1,t+tot+1);
    tot=unique(t+1,t+tot+1)-t-1;
    for(int i=1;i<=n;i++)
    {
      L[i]=lower_bound(t+1,t+tot+1,(node){l[i],h[i]})-t;
      R[i]=lower_bound(t+1,t+tot+1,(node){r[i],h[i]})-t;
    }
    for(int len=1;len<=tot;len++)
      for(int i=1;i+len-1<=tot;i++)
      {
        int j=i+len-1,maxn=0,x,y;f[i][j]=inf;
        for(int k=1;k<=n;k++)
          if(i<=L[k]&&R[k]<=j&&w[k]>maxn)
            x=L[k],y=R[k],maxn=w[k];
        if(!maxn){f[i][j]=0;continue;}
        for(int k=x;k<=y;k++)
          f[i][j]=min(f[i][j],f[i][k-1]+maxn+f[k+1][j]);
      }
    printf("%I64d\n",f[1][tot]);
  }
  return 0;
}

G Variance-MST(hdu 6368)

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

题解
最小方差生成树。
以前写过一道最小极差生成树,贴了发代码然后T了(雾)。
后来想想好像不太对。
首先最小方差生成树肯定是一段连续的边构成的最小生成树。
一种暴力做法是枚举平均数,根据平均数确定边权然后上mst,显然T飞,不过可以给我们思路。
对于两条冲突的边a和b,假设w[a]

#include
#define ll long long
#define ld long double
#define N 400010
#define inf 2100000000
#define iinf 9999999999999999.0
#define mod 998244353
using namespace std;
int Tx,n,m,tot,fa[N],L[N],R[N],pa[N];
ll sum1,sum2,ans;ld res;
struct node{int c[2],fa,rev,minn,num,pos;}t[N];
struct edge{
  int a,b,c;
  bool operator<(const edge &p)const{return cint x,y,inv;
  bool operator<(const info &p)const{return yy;}
}s[N];
int Pow(int a,int b)
{
  int res=1;
  while(b)
  {
    if(b&1)res=(ll)res*a%mod;
    a=(ll)a*a%mod;b>>=1;
  }
  return res;
}

class lct
{
  void pushdown(int x)
  {
    if(!t[x].rev)return;
    int lc=t[x].c[0],rc=t[x].c[1];
    if(lc)t[lc].rev^=1,swap(t[lc].c[0],t[lc].c[1]);
    if(rc)t[rc].rev^=1,swap(t[rc].c[0],t[rc].c[1]);
    t[x].rev=0;
  }
  void update(int x)
  {
    int lc=t[x].c[0],rc=t[x].c[1];
    t[x].minn=t[x].num;t[x].pos=x;
    if(lc&&t[lc].minnx].minn)
      t[x].minn=t[lc].minn,t[x].pos=t[lc].pos;
    if(rc&&t[rc].minnx].minn)
      t[x].minn=t[rc].minn,t[x].pos=t[rc].pos;
  }
  void rotate(int x,int k)
  {
    int y=t[x].fa,f=(t[t[y].fa].c[1]==y);
    pushdown(y);pushdown(x);
    if(!t[y].fa)fa[x]=fa[y];
    t[x].fa=t[y].fa;t[t[y].fa].c[f]=x;
    t[y].fa=x;t[y].c[k]=t[x].c[k^1];
    t[t[y].c[k]].fa=y;t[x].c[k^1]=y;
    update(y);
  }
  void splay(int x)
  {
    while(t[x].fa)
    {
      int y=t[x].fa,f=t[y].c[1]==x;
      if(!t[y].fa)rotate(x,f);
      else
      {
        if(f==(t[t[y].fa].c[1]==y))rotate(y,f),rotate(x,f);
        else rotate(x,f),rotate(x,f^1);
      }
    }
    pushdown(x);update(x);
  }
  void access(int x)
  {
    for(int y=0;x;y=x,x=fa[x])
    {
      splay(x);
      t[t[x].c[1]].fa=0;fa[t[x].c[1]]=x;
      t[x].c[1]=y;t[y].fa=x;fa[y]=0;
      update(x);
    }
  }
  int lca(int x,int y)
  {
    access(x);
    for(splay(y);fa[y];splay(y))y=fa[y];
    return y;
  }
  public:
  void link(int x,int y)
  {
    access(x);splay(x);fa[x]=y;
    t[x].rev^=1;swap(t[x].c[0],t[x].c[1]);
  }
  void cut(int x,int y)
  {
    access(x);splay(y);
    if(fa[y]==x)swap(x,y);splay(x);
    t[t[x].c[0]].fa=0;fa[t[x].c[0]]=0;
    t[x].c[0]=fa[x]=0;update(x);
  }
  int qry(int x,int y)
  {
    int p=lca(x,y),pos,res=p;
    access(x);splay(p);pos=t[p].c[1];
    if(pos&&t[pos].minnpos].pos;
    access(y);splay(p);pos=t[p].c[1];
    if(pos&&t[pos].minnpos].pos;
    return res;
  }
}T;
int find(int x)
{
  if(pa[x]==x)return x;
  return pa[x]=find(pa[x]);
}

int main()
{
  int a,b,c,x,y,p;
  scanf("%d",&Tx);
  while(Tx--)
  {
    scanf("%d%d",&n,&m);res=iinf;tot=0;
    for(int i=1;i<=m;i++)R[i]=inf;
    for(int i=1;i<=n;i++)pa[i]=i;
    for(int i=1;i<=n+m;i++)
    {
      t[i].c[0]=t[i].c[1]=t[i].rev=0;
      t[i].pos=i;t[i].fa=fa[i]=0;
      if(i<=n)t[i].minn=t[i].num=m+1;
      else t[i].minn=t[i].num=i-n;
    }
    for(int i=1;i<=m;i++)
      scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
    sort(e+1,e+m+1);
    for(int i=1;i<=m;i++)
    {
      a=e[i].a;b=e[i].b;c=e[i].c;
      x=find(a);y=find(b);
      if(x!=y)L[i]=-inf,pa[y]=x;
      else
      {
        p=T.qry(a,b)-n;R[p]=L[i]=e[p].c+c;
        T.cut(e[p].a,p+n);T.cut(e[p].b,p+n);
      }
      T.link(a,i+n);T.link(b,i+n);
    }
    for(int i=1;i<=m;i++)
    {
      s[++tot]=(info){e[i].c,L[i],1};
      s[++tot]=(info){e[i].c,R[i],-1};
    }
    sort(s+1,s+tot+1);sum1=0;sum2=0;
    for(int i=1,cnt=0;i<=tot;)
    {
      int pos=s[i].y;
      for(;s[i].y==pos;i++)
      {
        sum1+=s[i].inv*s[i].x;cnt+=s[i].inv;
        sum2+=(ll)s[i].inv*s[i].x*s[i].x;
      }
      if(cnt!=n-1)continue;
      ld tmp=(ld)sum1*sum1/cnt+sum2-(ld)2*sum1*sum1/cnt;
      if(tmp>=res)continue;res=tmp; 
      ll num=Pow(cnt,mod-2),A=sum1%mod*num%mod;
      ans=(A*A%mod*cnt%mod+sum2%mod-2*A*sum1%mod)*num%mod;
    } 
    printf("%d\n",(ans+mod)%mod);
  }
  return 0;
} 

J Chopping hands(hdu 6371)

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

题解
n之后21,一看就是一道爆搜题。。。
先写了一发2^n的大暴搜加上O(nlogn)求中位数,然后T飞。
把中位数改成O(n)求,然后T飞。
改成一遍搜一遍拿棵线段树维护中位数,然后T飞。
把搜索的顺序换一下,先搜卡牌多的卡组后搜卡牌少的卡组,然后T飞。
跑去看题解了,然后发现可以倒着搜,拿个链表维护中位数。
每次链表删除一个数很简单,回溯的时候其实就是撤销之前的删除操作,也很简单。
然后就过了。

代码

#include
#define inf 2147483647
#define N 205
using namespace std;
int Tx,n,m,ans,sum,mid,s[N],A;
int tot,Tnum,num[N],po[N],cnt[N];
int len[N],t[N][N],pos[N],v[N][N],pre[N],nxt[N];
inline bool cmp(const int &a,const int &b){return len[a]>len[b];} 

void link(int x)
{
  nxt[pre[x]]=x;
  pre[nxt[x]]=x;tot++;
  if(tot&1){if(xelse if(x>A)A=nxt[A];
}

void cut(int x)
{
  if(tot&1){if(x<=A)A=nxt[A];}
  else if(x>=A)A=pre[A];
  nxt[pre[x]]=nxt[x];
  pre[nxt[x]]=pre[x];tot--;
}

void dfs(int y,int sum)
{
  if(y>n)
  {
    if(!tot)return;
    if(tot&1)ans=max(ans,num[A]*2-sum);
    else ans=max(ans,num[A]+num[pre[A]]-sum);
    return;
  }
  int x=po[y];dfs(y+1,sum);
  for(int i=0,p;ix];++i)
    p=t[x][i],cut(v[p][--pos[p]]);
  dfs(y+1,sum-s[x]);
  for(int i=len[x]-1,p;i>=0;--i)
    p=t[x][i],link(v[p][pos[p]++]);
}

int main()
{
  scanf("%d",&Tx);
  while(Tx--)
  {
    scanf("%d%d",&n,&m);ans=-inf;tot=0;sum=0;
    for(int i=1;i<=n;++i)scanf("%d",&s[i]),sum+=s[i];
    for(int i=1;i<=n;++i)
    {
      scanf("%d",&len[i]);po[i]=i;
      for(int j=0;j"%d",&t[i][j]);
    }
    sort(po+1,po+n+1,cmp);
    for(int i=1;i<=m;++i)
    {
      scanf("%d",&pos[i]);
      for(int j=0;j<pos[i];++j)
        scanf("%d",&v[i][j]),num[++tot]=v[i][j];
    }
    sort(num+1,num+tot+1);
    for(int i=1;i<=tot;i++)cnt[i]=0;
    for(int i=1;i<=m;i++)
      for(int j=0;j<pos[i];j++)
        cnt[v[i][j]=lower_bound(num+1,num+tot+1,v[i][j])-num]++;
    for(int i=1;i<=tot;i++)cnt[i]+=cnt[i-1];
    for(int i=1;i<=m;i++)
      for(int j=0;j<pos[i];j++)v[i][j]=cnt[v[i][j]]--;
    for(int i=1;i<=tot;i++)pre[i]=i-1,nxt[i]=i+1;
    pre[tot+1]=tot;pre[0]=1;
    A=tot/2+1;dfs(1,sum);
    printf("%d\n",ans);
  }
  return 0; 
} 

K sacul(hdu 6372)

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

题解
根据lucas定理,c[i][j]modp 等价于把i和j搞成p进制求个组合数再乘起来。
c[i][j] mod p!=0 的条件是,将i和j拆解成p进制后,每一对组合数都不为0。
也就是说,对于每一块c(a,b),必须保证a>=b。
所以对于矩阵中的每一个点,HMBB[i][j]=1的条件是i和j分解成p进制之后每一位i都不小于j。我们把这样的i和j连边。
这样的话,f[i][j] 就表示j个i位p进制数,满足每个数的每一位不小于上一个数的方案数。
所以这就变成了一个组合计数问题。
显然每一位独立,f[i][j]=C(j+p,p-1)^i。
ans=ΣΣf[i][j],上一发等比数列求和就好了。

代码

#include
#define ll long long
#define mod 1000000007
#define N 2000010
using namespace std;
int T,c,n,k,tot,ans,prime[N],check[N],fac[N],inv[N];
const int maxn=2000000;
int Pow(int a,int b)
{
  int res=1;
  while(b)
  {
    if(b&1)res=(ll)res*a%mod;
    a=(ll)a*a%mod;b>>=1;
  }
  return res;
} 
int C(int a,int b){return (ll)fac[a]*inv[b]%mod*inv[a-b]%mod;}
int main()
{
  int tmp,res;
  for(int i=2;i<=maxn;i++)
  {
    if(!check[i])prime[++tot]=i;
    for(int j=1;j<=tot;j++)
    {
      if(i*prime[j]>maxn)break;
      check[i*prime[j]]=1;
      if(!(i%prime[j]))break;
    }
  }
  fac[0]=1;
  for(int i=1;i<=maxn;i++)fac[i]=(ll)fac[i-1]*i%mod;
  inv[maxn]=Pow(fac[maxn],mod-2);
  for(int i=maxn;i;i--)inv[i-1]=(ll)inv[i]*i%mod; 
  scanf("%d",&T);
  while(T--)
  {
    scanf("%d%d%d",&c,&n,&k);ans=0;
    for(int i=1;i<=k;i++)
    {
      tmp=C(i+prime[c],prime[c]-1);
      if(tmp==1)res=n;
      else res=(ll)(Pow(tmp,n)-1)*Pow(tmp-1,mod-2)%mod*tmp%mod;
      ans=(ans+res)%mod;
    }
    printf("%d\n",(ans+mod)%mod);
  }
  return 0; 
}

你可能感兴趣的:(题解,套题总结,dp及其优化,——link,cut,tree,数据结构,组合计数,搜索)