• haoi2018


    题解:

    题目相对其他省难一点

    不过弱省省选知识点都这么集中的么。。

    4道数学题。。。

    1.[HAOI2018]奇怪的背包

    这题考场做就gg了。。。

    其实我想到了那个性质。。 就是这个一定要是gcd的倍数

    但是我傻逼的觉得这个不对。。 因为xi都要>=0

    然后就看题解。。

    仔细想了一下 这可是模意义下啊??

    你要是负数你一直加p答案不是不变的么。。。

    然后有了这个性质我们考虑dp

    直接$f[i][j]$表示考虑了前i个数,gcd为j这个复杂度比较gg

    我们发现j一定是p的因数,所以离散化一下(hash表查找)

    设p的因数个数为M

    这个复杂度是$nMlogn$的 还是gg。。。

    我们再看一看 每次加入一个数实际上是与它取gcd

    而gcd一定也是y的因数 所以刚开始本质不同的数只有M种

    对于相同的数,不取就一种方案,取就是$(2^k-1)$

    然后这样dp就是$M^2logM$

    讲道理M的上限是sqrt的。。(不知道差了多少倍 程序要跑太久。。)

    但反正是能过了。。

    // luogu-judger-enable-o2
    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    #define rint register int
    #define IL inline
    #define rep(i,h,t) for (int i=h;i<=t;i++)
    #define dep(i,t,h) for (int i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    #define mid ((h+t)>>1)
    using namespace std;
    const int INF=1e9;
    namespace IO{
        char ss[1<<24],*A=ss,*B=ss;
        IL char gc()
        {
            return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
        }
        template<class T>void read(T &x)
        {
            rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
            while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f;
        }
        char sr[1<<24],z[20]; int Z,C=-1;
        template<class T>void wer(T x)
        {
            if (x<0) sr[++C]='-',x=-x;
            while (z[++Z]=x%10+48,x/=10);
            while (sr[++C]=z[Z],--Z);
        }
        IL void wer1()
        {
            sr[++C]=' ';
        }
        IL void wer2()
        {
            sr[++C]='
    ';
        }
        template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
        template<class T>IL void mina(T &x,T y) {if (x>y) x=y;}
        template<class T>IL T MAX(T x,T y) {return x>y?x:y;}
        template<class T>IL T MIN(T x,T y) {return x<y?x:y;}
    };
    using namespace IO;
    const int mo=5e6+7;
    struct Hash{
        int H[mo+10000],H1[mo+10000];
        IL void push(int x,int z)
        {
            int y=x%mo;
            while (H[y]!=0&&H[y]!=x) ++y;
            H[y]=x; H1[y]=z;
        }
        IL int query(int x)
        {
            int y=x%mo;
            while (H[y]!=0&&H[y]!=x) ++y;
            return H1[y];
        }
    }H;
    const int N=1e6+1e5;
    const int M=3.5e4+100;
    int a[N],f[N],pp[M];
    int g[2][M],now[M],now2[M],o[N];
    int gcd(int x,int y)
    {
        return (!y)?x:gcd(y,x%y);
    }
    const int mo2=1e9+7;
    IL void js(register int &x,register int y)
    {
        x+y>mo2?x=x+y-mo2:x=x+y;
    }
    int main()
    {
        int n,q,p;
        read(n); read(q); read(p);
        rep(i,1,n) read(a[i]);
        int k=sqrt(p);
        int cnt=0;
        rep(i,1,k)
          if (p%i==0) f[++cnt]=i;
        k=cnt;
        dep(i,k,1)
          if (p!=f[i]*f[i]) f[++cnt]=p/f[i];
        rep(i,1,cnt) H.push(f[i],i);
        rep(i,1,n) pp[H.query(gcd(a[i],p))]++;
        o[0]=1;
        rep(i,1,1000000) o[i]=o[i-1]*2%mo2;
        rep(i,1,1000000) o[i]=((o[i]-1)%mo2+mo2)%mo2;
        g[1][cnt]=1;
        rep(i,1,cnt)
        {
          int *g1=g[i%2],*g2=g[(i+1)%2];
          memset(g[(i+1)%2],0,sizeof(g[0][0])*(cnt+10));
          if (pp[i])
          rep(j,1,cnt)
            if (g1[j])
            {
                js(g2[H.query(gcd(f[i],f[j]))],(1ll*g1[j]*o[pp[i]])%mo2);
            }
          rep(j,1,cnt) js(g2[j],g1[j]);
        }
        memcpy(now,g[(cnt+1)%2],sizeof(g[(cnt+1)%2]));
        rep(i,1,cnt)
        {
          int w=p/f[i];
          int k=sqrt(w);
          rep(j,1,k)
            if (w%j==0)
            {
                int t=H.query(f[i]*j);
                if (t) js(now2[t],now[i]);
                if (j*j!=w)
                {
                    t=H.query(f[i]*w/j);
                    if (t) js(now2[t],now[i]);
                }
            }
        }
        rep(i,1,q)
        {
            int x; read(x);
            x=H.query(gcd(x,p));
            wer(now2[x]); wer2();
        }
        fwrite(sr,1,C+1,stdout);
        return 0;
    }

    2.[HAOI2018]反色游戏

    这个感觉思路其实不难 但好像并没有往这上面想

    第一感觉就是这个可以化成xor然后线性基高斯消元

    最后线性基中为0的点就可以任取 答案为2^k 注意还要判断合不合法 不合法直接为0

    然后发现就算没有多次询问就一组也过不了???

    然后我就觉得应该是有个黑科技我没学过。。。。

    先说一下部分分。。

    50分暴力高斯消元就可以了 ($n^4$)

    60分需要bitset优化($n^4/32$)

    // luogu-judger-enable-o2
    #include <bits/stdc++.h>
    using namespace std;
    #define rint register int
    #define IL inline
    #define rep(i,h,t) for (int i=h;i<=t;i++)
    #define dep(i,t,h) for (int i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    const int N=210;
    const int mo=1e9+7;
    int T,n,m;
    struct re{
      int a,b;
    };
    vector<re> ve[N];
    char c[N];
    bitset<N> B[N],now; 
    int ans[N],f[N],g[2000];
    bool cmp(int x,int y)
    {
      return x>y;
    }
    int main()
    {
      ios::sync_with_stdio(false);
      g[0]=1;
      rep(i,1,1000) g[i]=(g[i-1]*2)%mo; 
      cin>>T;
      rep(ttt,1,T)
      {
        cin>>n>>m;
        rep(i,1,n) ve[i].clear();
        rep(i,1,m)
        {
          int x,y;
          cin>>x>>y;
          ve[x].push_back((re){i,y}); ve[y].push_back((re){i,x});
        }
        cin>>c;
        rep(i,0,n-1) f[i+1]=c[i]-'0';
        rep(i,0,n)
        {
          bool tt=1;
          int c3=0;
          rep(j,1,n)
            for (int k=0;k<ve[j].size();k++)
              if (j==i||ve[j][k].b==i) c3++;
          c3/=2;
          rep(j,1,m) B[j]=0,ans[j]=0;
          rep(j,1,n)
          if (j!=i)
          {
            now=0; int ans2=f[j];
            for (int k=0;k<ve[j].size();k++)
              if (ve[j][k].b!=i) now[ve[j][k].a]=1;
            dep(k,m,1)
              if (now[k])
              {
                if (B[k]!=0)
                { 
                  now^=B[k],ans2^=ans[k];
                  if (now==0) break;
                }
                else
                { 
                  B[k]=now;
                  ans[k]=ans2;
                  break;
                }
              }
            if (now==0&&ans2) tt=0;
          }
          int cnt=0;
          rep(j,1,m) if (B[j]==0) cnt++;
          cnt-=c3;
          if (!tt) cout<<0<<" ";
          else cout<<g[cnt]<<" ";
        }
        cout<<endl;
      }
      return 0;
    }

    70分我不知道是什么东西。。。 我会线段树分治做到($n^3logn/32$) 但这个常数明显大而且肯定过不了70啊。。

    然后考虑一下正解

    发现这题要用线性基其实是假的

    一条边只能连两个节点啊。。。

    所以我们考虑合并

    如果合并之前两点已经有关系说明这条边就任意了

    于是简化一下要求的就是2^(n-m-联通块个数)

    另外怎么判断合不合法的充要条件是 如果黑点数为奇数就不合法

    来简单证明一下

    必要性:改变的颜色数是边数*2的 黑点为奇数显然不行

    充分性:如果改变了偶数个,每次我们就任取两个扩展一条他们之前的连边 如果重复就把边取反就可以了

    于是我们要支持删一个点求联通块个数和有无奇数个黑点

    分类讨论了

    3.

    由于后缀数组和后缀自动机有点忘了。。以后再补

    day2为啥只有两题。。

    4.[HAOI2018]苹果树

    我用了跟题解不太一样的方法 当然题解更加简单。。

    令$f(i)$表示i个点排成子树的方案数 (我刚开始并没有想到这个是n!)

    $f(i)=sum C(n,i)*f(i)*f(n-i)$

    令$g(i)$表示i个点排成子树到根的路径和

    $g(i)=sum$

    令$h(i)$表示i个点排成子树内部路径和

    $h(i)=sum$

    题解的方法也比较常见

    考虑每条边的贡献

    #include <bits/stdc++.h>
    using namespace std;
    #define rint register int
    #define IL inline
    #define rep(i,h,t) for (int i=h;i<=t;i++)
    #define dep(i,t,h) for (int i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    namespace IO{
        char ss[1<<24],*A=ss,*B=ss;
        IL char gc()
        {
            return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
        }
        template<class T>void read(T &x)
        {
            rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
            while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f;
        }
        char sr[1<<24],z[20]; int Z,CC=-1;
        template<class T>void wer(T x)
        {
            if (x<0) sr[++CC]='-',x=-x;
            while (z[++Z]=x%10+48,x/=10);
            while (sr[++CC]=z[Z],--Z); 
        }
        IL void wer1()
        {
            sr[++CC]=' ';
        }
        IL void wer2()
        {
            sr[++CC]='
    ';
        }
        template<class T>IL void mina(T &x,T y) { if (x>y) x=y;}
        template<class T>IL void maxa(T &x,T y) { if (x<y) x=y;}
        template<class T>IL T MIN(T x,T y){return x<y?x:y;}
        template<class T>IL T MAX(T x,T y){return x>y?x:y;}
    };
    using namespace IO;
    const int N=2050;
    int nn,mo;
    int C[N][N],f[N],g[N],h[N];
    IL void js(int &x,int y)
    {
        x=x+y>mo?x+y-mo:x+y;
    }
    int main()
    {
        read(nn); read(mo);
        int m=2000;
        rep(i,1,m)
        {
          C[i][0]=C[i][i]=1;
          rep(j,1,i-1)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
        }
        f[0]=f[1]=1;
        rep(i,2,nn)
        {
          int n=i-1;
          rep(j,0,n)
            js(f[i],1ll*f[j]*f[n-j]%mo*C[n][j]%mo);
          rep(j,0,n)
            js(g[i],2ll*g[j]%mo*C[n][j]%mo*f[n-j]%mo);
          js(g[i],1ll*f[i]*n%mo);
          rep(j,0,n)
            js(h[i],2ll*h[j]%mo*C[n][j]%mo*f[n-j]%mo);
          rep(j,0,n)
            js(h[i],(2ll*j%mo*(n-j)%mo*f[j]%mo*f[n-j]%mo+2ll*g[j]%mo*(n-j)%mo*f[n-j]%mo)%mo*C[n][j]%mo);
          js(h[i],g[i]);
        }
     //   rep(i,1,nn) cout<<f[i]<<" "<<g[i]<<" "<<h[i]<<endl; 
        cout<<h[nn]<<endl;
        return 0; 
    }

    5.[HAOI2018]染色

    这个题型比较经典

    首先我们考虑取出k个正好有s的

     $C(n,s)*C(n-s,s)*...*C(n-k,s) $然后这个可以化简一下

    然后因为这个已经保证了顺序 颜色只需要$C(m,k)$就行了(不是$A(s,k)$)

    令$F(n,m)$表示n个点用m种颜色染,没有为k的方案数

    这个比较显然可以容斥来做 式子太长不想写。。。

    列出式子再带进去

    我的ntt方法好像和网上不太一样

    $ans=f(i)*sum{g(j)*h(i+j)}$

    然后我们变换一下 令$p(i)=h(t-i)$

    这样就是卷积形式了

    写代码的时候正推预处理了1e7的inv

    然后发现这个跑的贼慢。。直接exgcd算比这个快多了

    有一个常数更小的方法是先计算$(n!)$的逆元然后再倒推回去

    这样就不需要除法和取模了

    好久没打ntt。。

    ntt的n 应该是算出结果的最高项次数

    刚开始没算0的情况

    这种题目基本最简单的数据对了就对了

    所以可以自己造比较好手模的数据

    代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define rint register int
    #define IL inline
    #define rep(i,h,t) for(int i=h;i<=t;i++)
    #define dep(i,t,h) for(int i=t;i>=h;i--)
    #define ll long long
    #define me(x) memset(x,0,sizeof(x))
    namespace IO{
        char ss[1<<24],*A=ss,*B=ss;
        IL char gc()
        {
            return A==B&&(B=(A=ss)+fread(ss,1,1<<24,stdin),A==B)?EOF:*A++;
        }
        template<class T> void read(T &x)
        {
            rint f=1,c; while (c=gc(),c<48||c>57) if (c=='-') f=-1; x=(c^48);
            while (c=gc(),c>47&&c<58) x=(x<<3)+(x<<1)+(c^48); x*=f; 
        }
        char sr[1<<24],z[20]; int Z,C=-1;
        template<class T>void wer(T x)
        {
            if (x<0) sr[++C]='-',x=-x;
            while (z[++Z]=x%10+48,x/=10);
            while (sr[++C]=z[Z],--Z);
        }
        IL void wer1()
        {
            sr[++C]=' ';
        }
        IL void wer2()
        {
            sr[++C]='
    ';
        }
        template<class T>IL void maxa(T &x,T y) {if (x<y) x=y;}
        template<class T>IL void mina(T &x,T y) {if (x>y) x=y;} 
        template<class T>IL T MAX(T x,T y){return x>y?x:y;}
        template<class T>IL T MIN(T x,T y){return x<y?x:y;}
    };
    using namespace IO;
    const int mo=1004535809;
    const int N=1e6+10;
    const int M=1e6;
    const int N1=1e7+10;
    const int N2=1e7;
    int ww[N1],inv[N1],jc[N1],jc2[N1];
    int cj[N],nicj[N];
    int f[N],g[N],h[N];
    void gcd(int x,int y,int &a,int &b)
    {
        if (!y)
        {
            a=1; b=0; return;
        }
        gcd(y,x%y,b,a);
        b-=a*(x/y);
    }
    int ksm(int x,int y)
    {
        if (y==0) return(1);
        if (y==1) return(x);
        int k=ksm(x,y/2);
        k=(1ll*k*k)%mo;
        if (y&1) k=(1ll*k*x)%mo;
        return k;
    }
    IL void js(register int &x,register int y)
    {
        x=x+y>=mo?x+y-mo:x+y;
    }
    int l,n1,r[N],w[N],x[N],y[N],G=3;
    void ntt(int *a,int o)
    {
        //n n1别写错 
        for (int i=0;i<n1;i++)
          if (i>r[i]) swap(a[i],a[r[i]]);
        for (int i=1;i<n1;i*=2)
        {
            int    wn=ksm(G,(mo-1)/(i*2)); w[0]=1;
            rep(j,1,i-1) w[j]=(1ll*w[j-1]*wn)%mo;
            for(int j=0;j<n1;j+=(i*2))
            {
                rint *x=a+j,*y=a+i+j;
                for (rint k=0;k<i;k++)
                {
                    int t=1ll*w[k]*y[k]%mo;
                    y[k]=x[k]-t<0?x[k]-t+mo:x[k]-t;
                    x[k]=x[k]+t>mo?x[k]+t-mo:x[k]+t;
                }
            }      
        }
        if (o==-1)
        {
            reverse(&a[1],&a[n1]);
            for (int i=0,inv=ksm(n1,mo-2);i<n1;i++)
              a[i]=1ll*a[i]*inv%mo;
        }
    }
    int t;
    void query()
    {
        l=0;
        for (n1=1;n1<=t*2;n1<<=1) l++;
        for (int i=0;i<n1;i++) r[i]=(r[i/2]/2)|((i&1)<<(l-1));
        ntt(f,1); ntt(g,1);
        for (int i=0;i<n1;i++) f[i]=(1ll*f[i]*g[i])%mo;
        ntt(f,-1);
    }
    int main()
    {
        freopen("1.in","r",stdin);
        freopen("1.out","w",stdout);
        int n,m,s;
        read(n); read(m); read(s);
        rep(i,0,m) read(ww[i]);
        inv[1]=1;
        rep(i,2,N2) inv[i]=(-1ll*(mo/i)*inv[mo%i]%mo+mo)%mo;
        jc2[0]=1; jc[0]=1; cj[0]=1; nicj[0]=1;
        rep(i,1,N2) jc[i]=1ll*jc[i-1]*i%mo,jc2[i]=1ll*jc2[i-1]*inv[i]%mo;
        int x,y;
        gcd(jc[s],mo,x,y); 
        x=(x+mo)%mo;
        rep(i,1,M)
        {  
          cj[i]=1ll*cj[i-1]*jc[s]%mo;
          nicj[i]=1ll*nicj[i-1]*x%mo;
        }
        t=MIN(m,n/s);
        rep(i,0,t)
          if (i%2==0) 
            f[i]=1ll*nicj[i]*jc2[i]%mo;
          else f[i]=(-1ll*nicj[i]*jc2[i]%mo+mo)%mo;
        rep(i,0,t)
          g[i]=1ll*ksm((m-i),n-i*s)*jc2[n-i*s]%mo*jc2[m-i]%mo;
        reverse(g,g+t+1);
        query(); 
        rep(i,0,t) h[i]=(f[t-i]+mo)%mo;
    /*    rep(i,0,t)
          rep(j,i,t)
            js(h[j-i],1ll*f[i]*g[j]%mo); */
        int ans=0;
        rep(i,0,t)
        {
            int t1=1ll*ww[i]*jc[n]%mo*nicj[i]%mo*jc[m]%mo*jc2[i]%mo;
            t1=1ll*t1*h[i]%mo;
            js(ans,t1);
        }
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    EL 自定义函数
    Linux 软件安装管理
    Linux 网络环境查看命令
    Linux 用户和用户组的命令
    Linux 用户和用户组进阶命令
    Linux 用户和用户组的基本命令
    将博客搬至CSDN
    U盘做系统启动盘(PE)时的文件格式选择 HDD ZIP FDD
    STM32 的几种输入输出模式
    define 中强制类型转换 && 浮点数后面带f
  • 原文地址:https://www.cnblogs.com/yinwuxiao/p/10029649.html
Copyright © 2020-2023  润新知