zoukankan      html  css  js  c++  java
  • LOJ 3056 「HNOI2019」多边形——模型转化+树形DP

    题目:https://loj.ac/problem/3056

    只会写暴搜。用哈希记忆化之类的。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<map>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    const int N=20,M=1e6+5,bs=10009,md=1e9+9,mod=1e9+7,INF=M;
    int upt(int x){while(x>=mod)x-=mod;while(x<0)x+=mod;return x;}
    
    int W,n,lm,tot;
    struct Dt{
      int x,y;
      Dt(int x=0,int y=0):x(x),y(y) {}
      bool operator< (const Dt &b)const
      {return x==b.x?y<b.y:x<b.x;}
      bool operator== (const Dt &b)const
      {return x==b.x&&y==b.y;}
    }c[M];
    struct Node{
      Dt a[N];
      void sort(){std::sort(a+1,a+lm+1);}
      int hs()
      {
        int ret=0;
        for(int i=1;i<=lm;i++)
          {
        ret=((ll)ret*bs+a[i].x)%md;
        ret=((ll)ret*bs+a[i].y)%md;
          }
        return ret;
      }
    }s[N];
    map<int,int> mp;
    Node cz(Node u,Dt k)
    {
      int a=k.x,b=0,c=k.y,d=0;
      for(int i=1;i<=lm;i++)
        {
          Dt tp=u.a[i]; if(tp.x>a)break;
          if(tp.x==a&&tp.y<c)b=tp.y;
          if(tp.x==a&&tp.y>c&&!d)d=tp.y;
        }
      if(!b) b=a+1;
      if(!d)
        {
          int ty=0;
          for(int i=1;i<=lm;i++)
        if(u.a[i].x==a){ty=u.a[i].y;break;}
          if(ty<a) d=ty; else d=(a==1?n:a-1);//ty<a!!!
        }
      if(b>d){u.a[1].x=0;return u;}///
      for(int i=1;i<=lm;i++)
        {
          Dt tp=u.a[i]; if(tp.x!=a||tp.y!=c)continue;
          u.a[i].x=b; u.a[i].y=d;
          for(int j=i+1;j<=lm;j++)
        if(u.a[j]<u.a[j-1])swap(u.a[j],u.a[j-1]);
          for(int j=i-1;j;j--)
        if(u.a[j+1]<u.a[j])swap(u.a[j],u.a[j+1]);
        }
      return u;
    }
    Dt dfs(Node cr)
    {
      int h=cr.hs(); if(mp.count(h))return c[mp[h]];
      Dt lj=Dt(INF,0);
      for(int i=1;i<=lm;i++)
        {
          if(cr.a[i].y==n)continue;
          Node to=cz(cr,cr.a[i]); if(!to.a[1].x)continue;
          Dt d=dfs(cz(cr,cr.a[i])); d.x++;
          if(d.x==lj.x)lj.y=upt(lj.y+d.y);
          else if(d.x<lj.x)lj=d;
        }
      if(lj.x==INF)lj=Dt(0,1);
      mp[h]=++tot; return c[tot]=lj;
    }
    void solve()
    {
      lm=n-3;
      for(int i=1,u,v;i<=lm;i++)
        {
          u=rdn(); v=rdn(); if(u>v)swap(u,v);
          s[0].a[i]=Dt(u,v);
        }
      s[0].sort();
      int m=rdn();
      for(int i=1,u,v;i<=m;i++)
        {
          u=rdn();v=rdn(); if(u>v)swap(u,v);
          s[i]=cz(s[0],Dt(u,v));
        }
      dfs(s[0]);
      for(int i=0;i<=m;i++)
        {
          Dt d=c[mp[s[i].hs()]];
          if(!W)printf("%d
    ",d.x);
          else printf("%d %d
    ",d.x,d.y);
        }
    }
    int main()
    {
      W=rdn(); n=rdn();
      if(n<=14)solve();
      return 0;
    }
    View Code

    应该更大胆一些。果然就是最终每条边都与 n 点相连、每次能把一条边变成这样。

    那么第一问的答案就是 ( n-3 ) - ( 初始就与 n 相连的边数 ) 。

    并且这样的话,可以看出一个二叉树森林。就是把一条边旋转成与 n 相连之后,分出两个部分,两个部分里的边在该边旋转之后才能旋转。

    设这条边的左孩子有 a 步、右孩子有 b 步,自己的方案就是 ( dp[ ls ] * dp[ rs ] * inom{a+b}{a} ) 。 m=0 的这样做一下就行了。

    一开始旋转一条边是 rotate 操作。(从“出现的边是什么”的角度来看,确实是 rotate)

    如果旋转的是某个二叉树的根,就是令答案步数 -1 , 把该二叉树从根断成两个二叉树。

    如果旋转的不是根,发现对上面的影响只有 ( dp[ ] ) 的改变,所以除掉原来的再乘上现在的。

    如果旋转的是根,不仅 ( dp[ ] ) 变了,一些 ( inom{a+b}{a} ) 也变了,所以不能除掉再乘。维护前缀和后缀答案即可。

    把边按 “左端点递增、右端点递减” (左端点指标号小的点)排序,找森林结构的时候,对于边的区间 [ L , R ] , L 是第一个要旋转的,然后左端点在 L 的右端点之前(严格)的边是自己的左孩子,其他是右孩子。找分界的时候自己用了 lower_bound ,反正每次剥掉一个 L ,一共调用 n 次 lower_bound ,而且调用的数组大小还会变小,所以复杂度还可以。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    int rdn()
    {
      int ret=0;bool fx=1;char ch=getchar();
      while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
      while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
      return fx?ret:-ret;
    }
    const int N=1e5+5,mod=1e9+7;
    int pw(int x,int k)
    {int ret=1;while(k){if(k&1)ret=(ll)ret*x%mod;x=(ll)x*x%mod;k>>=1;}return ret;}
    
    int n,fa[N],c[N][2],sta[N],top,rk[N],jc[N],jcn[N],ans;
    struct Node{
      int x,y;
      Node(int x=1,int y=0):x(x),y(y) {}
      bool operator< (const Node &b)const
      {return x==b.x?y>b.y:x<b.x;}
    }ed[N],dp[N],pr[N],sc[N];
    int C(int n,int m){return (ll)jc[n]*jcn[m]%mod*jcn[n-m]%mod;}
    Node mrg(Node u,Node v)
    { u.y+=v.y; u.x=(ll)u.x*v.x%mod*C(u.y,v.y)%mod; return u;}
    void pshp(int cr)
    {
      dp[cr]=Node(1,0);
      if(c[cr][0])dp[cr]=mrg(dp[cr],dp[c[cr][0]]);
      if(c[cr][1])dp[cr]=mrg(dp[cr],dp[c[cr][1]]);
      dp[cr].y+=(cr!=0);
    }
    void ini_dfs(int L,int R)
    {
      if(L==R){pshp(L);return;}
      if(ed[L+1].x!=ed[L].x)
        {
          ini_dfs(L+1,R);
          c[L][1]=L+1; fa[L+1]=L; pshp(L); return;
        }
      Node d=Node(ed[L+1].y,n+1);
      int mid=lower_bound(ed+L+1,ed+R+1,d)-ed;
      ini_dfs(L+1,mid-1); c[L][0]=L+1; fa[L+1]=L;
      if(mid<=R){ ini_dfs(mid,R); c[L][1]=mid; fa[mid]=L;}
      pshp(L);
    }
    void init()
    {
      jc[0]=1;for(int i=1;i<=n;i++)jc[i]=(ll)jc[i-1]*i%mod;
      jcn[n]=pw(jc[n],mod-2);
      for(int i=n-1;i>=0;i--)jcn[i]=(ll)jcn[i+1]*(i+1)%mod;//before!!!
    
      int lm=n-3;
      sort(ed+1,ed+lm+1);
      for(int i=1,lst=1;i<=lm;i++)
        {
          while(i<=lm&&ed[i].y!=n)i++;
          if(lst<i)
        {
          ini_dfs(lst,i-1);
          sta[++top]=lst; rk[lst]=top;
          pr[top]=mrg(pr[top-1],dp[lst]);
        }
          if(i<=lm)///way:1, siz:0
        {
          sta[++top]=i; rk[i]=top;
          pr[top]=mrg(pr[top-1],dp[i]);
        }
          lst=i+1;
        }
      for(int i=top;i;i--)sc[i]=mrg(sc[i+1],dp[sta[i]]);
    }
    int rotate(int x)
    {
      int y=fa[x],d=(x==c[y][1]);
      Node nx=dp[x], ny=dp[y]; c[y][d]=c[x][!d]; c[x][!d]=y;
      pshp(y); pshp(x);
      int ret=(ll)dp[0].x*pw(ny.x,mod-2)%mod*dp[x].x%mod;
      c[x][!d]=c[y][d]; c[y][d]=x; dp[x]=nx; dp[y]=ny;
      return ret;
    }
    int main()
    {
      int W=rdn(); n=rdn(); int lm=n-3;
      for(int i=1,u,v;i<=lm;i++)
        { u=rdn();v=rdn();if(u>v)swap(u,v);ed[i]=Node(u,v);}
      ans=lm;
      for(int i=1;i<=lm;i++)if(ed[i].y==n)ans--;
      init(); dp[0]=pr[top];
      if(!W)printf("%d
    ",ans);
      else printf("%d %d
    ",ans,dp[0].x);
      int m=rdn();
      for(int i=1,u,v,k;i<=m;i++)
        {
          u=rdn();v=rdn();if(u>v)swap(u,v);
          k=lower_bound(ed+1,ed+lm+1,Node(u,v))-ed;
          if(!fa[k])
        {
          Node d=mrg(pr[rk[k]-1],sc[rk[k]+1]);
          if(c[k][0])d=mrg(d,dp[c[k][0]]);
          if(c[k][1])d=mrg(d,dp[c[k][1]]);
          if(!W)printf("%d
    ",ans-1);
          else printf("%d %d
    ",ans-1,d.x);
        }
          else
        {
          if(!W)printf("%d
    ",ans);
          else printf("%d %d
    ",ans,rotate(k));
        }
        }
      return 0;
    }
    View Code
  • 相关阅读:
    hdu 6201 dfs
    Oulipo POJ
    Kitchen Measurements UVALive
    Surf Gym
    hoj 13969 Racing Gems
    分块
    分块学习资料
    Jam's problem again HDU
    树的点分治
    Census UVA
  • 原文地址:https://www.cnblogs.com/Narh/p/10722156.html
Copyright © 2011-2022 走看看