• CQOI2018做题记录


    T1.破解D-H协议
    传送门

    这个题就是BSGS的板子题……
    然后这里补充一点嘛,就是第二重循环的枚举范围。我们是在枚举(a^{tm-y}),把tm换成i,这个的最大值就是(i - (m - 1) < c),即(i < c + m - 1)

    #include<bits/stdc++.h>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 200005;
    const int mod = 999979;
    
    int read()
    {
        int ans = 0,op = 1;char ch = getchar();
        while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
        while(ch >= '0' && ch <= '9') ans *= 10,ans += ch - '0',ch = getchar();
        return ans * op;
    }
    
    int g,P,n,A,B;
    
    struct Hash
    {
       int sta[M],top,head[M<<3],ecnt,num[M],val[M],nxt[M];
       void init(){ecnt = 0;while(top) head[sta[top--]] = 0;}
       void insert(int x,int y)
       {
          int h = x % mod;
          for(int i = head[h];i;i = nxt[i]) if(num[i] == x) {val[i] = y;return;}
          if(!head[h]) sta[++top] = h;
          nxt[++ecnt] = head[h],head[h] = ecnt;
          num[ecnt] = x,val[ecnt] = y;
       }
       int query(int x)
       {
          int h = x % mod;
          for(int i = head[h];i;i = nxt[i]) if(num[i] == x) return val[i];
          return -1;
       }
    }H;
    
    int mul(int a,int b,int t){return 1ll * a * b % t;}
    int qpow(int a,int b,int t)
    {
       int p = 1;
       while(b)
       {
          if(b & 1) p = mul(p,a,t);
          a = mul(a,a,t),b >>= 1;
       }
       return p;
    }
    int gcd(int a,int b){return b ? gcd(b,a%b) : a;}
    int exgcd(int a,int b,int &x,int &y)
    {
       if(!b){x = 1,y = 0;return a;}
       int d = exgcd(b,a%b,y,x);
       y -= a / b * x;
       return d;
    }
    
    int inv(int a,int b)
    {
       int x,y;
       exgcd(a,b,x,y);
       return (x % b + b) % b;
    }
    
    int BSGS(int a,int b,int c)
    {
       int cnt = 0,G,d = 1;
       while((G = gcd(a,c)) != 1)
       {
          if(b % G) return -1;
          cnt++,b /= G,c /= G,d = mul(d,a/G,c);
       }
       b = mul(b,inv(d,c),c);
       H.init();
       int s = sqrt(c),p = 1;
       rep(i,0,s-1)
       {
          if(p == b) return i + cnt;
          H.insert(mul(p,b,c),i),p = mul(p,a,c);
       }
       int q = p,t;
       for(int i = s;i <= c + s - 2;i += s)
       {
          t = H.query(q);
          if(t != -1) return i - t + cnt;
          q = mul(q,p,c);
       }
       return -1;
    }
    
    int main()
    {
       g = read(),P = read(),n = read();
       rep(i,1,n)
       {
          A = read(),B = read();
          int a = BSGS(g,A,P);
          int b = BSGS(g,B,P);
          printf("%d
    ",qpow(g,mul(a,b,P-1),P));
       }
       return 0;
    }
    

    T2.社交网络
    传送门
    这个题是有向图有根树生成树计数,根必须为1.
    其实和无向图的生成树计数基本是一样的……就是这次的基尔霍夫矩阵按照有向图来建造。
    然后因为根是1,所以我们把矩阵的第一行和第一列删掉,剩下的用高斯消元就可以了。

    #include<bits/stdc++.h>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 255;
    const int mod = 1e4+7;
    
    int read()
    {
        int ans = 0,op = 1;char ch = getchar();
        while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
        while(ch >= '0' && ch <= '9') ans *= 10,ans += ch - '0',ch = getchar();
        return ans * op;
    }
    
    int n,m,x,y,f[M][M];
    int inc(int a,int b){return (a + b) % mod;}
    int mul(int a,int b){return 1ll * a * b % mod;}
    int qpow(int a,int b)
    {
       int p = 1;
       while(b)
       {
          if(b & 1) p = mul(p,a);
          a = mul(a,a),b >>= 1;
       }
       return p;
    }
    
    int gauss()
    {
       int ans = 1;
       rep(i,2,n)
       {
          rep(j,i+1,n) if(f[j][i]) {swap(f[j],f[i]),ans = mod - ans;break;}
          if(!f[i][i]) return 0;
          ans = mul(ans,f[i][i]);
          int inv = qpow(f[i][i],mod-2);
          rep(j,i+1,n)
          {
         int cur = mul(f[j][i],inv);
         rep(k,i,n) f[j][k] = inc(f[j][k],mod - mul(f[i][k],cur));
          }
       }
       return ans;
    }
    
    int main()
    {
       n = read(),m = read();
       rep(i,1,m) x = read(),y = read(),f[x][x]++,f[x][y]--;
       printf("%d
    ",gauss());
       return 0;
    }
    

    T3.交错序列
    传送门
    个人认为是最难的一道了……
    (x^ay^b)难以处理,于是我们转化为((n-y)^ay^b),那么展开以后就是(sum_{i=0}^a(-1)^{a-i}n^iC_a^iy^{a+b-i})
    于是我们只要求出(y^i)的和就可以了。我们可以考虑DP。
    (dp[i][j][0/1])表示当前选取到长度为i,当前位为0/1,当前序列中1的个数的j次方之和。
    然后对于0/1变0的情况是没有什么变化的,就是(dp[i][j][0] += dp[i-1][j][0/1])
    如果这一位是1的话,根据二项式定理就可以得到(dp[i][j][1] += sum_{k=0}^jC_j^kdp[i-1][k][0])
    于是这个可以用矩阵乘法优化。然后我写完之后还被卡常了……把矩乘内部开成ll,最内一层循环不取模就过了……
    最后就按照上面的式子计算答案。

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 200005;
    const int N = 185;
    const int mod = 999979;
    
    int read()
    {
       int ans = 0,op = 1;char ch = getchar();
       while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
       while(ch >= '0' && ch <= '9') ans *= 10,ans += ch - '0',ch = getchar();
       return ans * op;
    }
    
    int n,a,b,m,siz,C[N][N],ans;
    int inc(int a,int b){return (a + b) % m;}
    int mul(int a,int b){return 1ll * a * b % m;}
    
    struct matrix
    {
       ll f[N][N];
       matrix(){memset(f,0,sizeof(f));}
       void init(){rep(i,0,siz-1) f[i][i] = 1;}
       friend matrix operator * (const matrix &a,const matrix &b)
       {
          matrix c;
          rep(i,0,siz-1)
          rep(j,0,siz-1)
          {	 
    	 rep(k,0,siz-1) c.f[i][j] += a.f[i][k] * b.f[k][j];
    	 c.f[i][j] %= m;
          }
          return c;
       }
       friend matrix operator ^ (matrix a,int b)
       {
          matrix p;
          p.init();
          while(b)
          {
    	 if(b & 1) p = p * a;
    	 a = a * a,b >>= 1;
          }
          return p;
       }
    }G;
    
    int main()
    {
       n = read(),a = read(),b = read(),m = read(),siz = (a+b+1) << 1;
       C[0][0] = 1;
       rep(i,1,a+b)
       {
          C[i][0] = 1;
          rep(j,1,i) C[i][j] = inc(C[i-1][j-1],C[i-1][j]);
       }
       rep(i,0,a+b)
       {
          G.f[i][i] = G.f[i][i+a+b+1] = 1;
          rep(j,0,i) G.f[i+a+b+1][j] = C[i][j];
       }
       G = G ^ n;
       int cur = 1;
       rep(i,0,a)
       {
          int now = ((a - i) & 1) ? -1 : 1;
          ans = inc(ans,mul(mul(cur,C[a][i]),mul(now,inc(G.f[a+b-i][0],G.f[siz-1-i][0]))));
          cur = mul(cur,n);
       }
       printf("%d
    ",(ans % m + m) % m);
       return 0;
    }
    

    T4.解锁屏幕
    传送门
    这题挺好玩啊。
    但是可以显而易见的看出是状压DP。然后转移也很好想……
    (dp[i][j])表示选取集合i,末尾为j的方案数。转移的时候枚举点,按照题中条件判断即可。然后注意在两点连线上的点需要预处理。
    然后注意答案要连四个及以上的点,模数是1e8+7。其实很奇怪这玩意复杂度最坏是(2^nn^2)的,但是就是过了……

    // luogu-judger-enable-o2
    #include<bits/stdc++.h>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 1000005;
    const int mod = 1e8+7;
    
    int read()
    {
        int ans = 0,op = 1;char ch = getchar();
        while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
        while(ch >= '0' && ch <= '9') ans *= 10,ans += ch - '0',ch = getchar();
        return ans * op;
    }
    
    struct dot
    {
       double x,y;
    }a[25];
    
    int n,G[25][25],dp[1<<20][21],ans;
    int inc(int a,int b){return (a+b) % mod;}
    double slope(const int &f,const int &g) {return (a[g].y - a[f].y) / (a[g].x - a[f].x);}
    bool in(int i,int j,int k)
    {
       if((a[k].y >= a[i].y && a[k].y <= a[j].y) || (a[k].y >= a[j].y && a[k].y <= a[i].y))
       {
          if((a[k].x >= a[i].x && a[k].x <= a[j].x) || (a[k].x >= a[j].x && a[k].x <= a[i].x)) return 1;
       }
       return 0;
    }
    
    int count(int x)
    {
       int cur = 0;
       while(x) cur += x & 1,x >>= 1;
       return cur;
    }
    
    int main()
    {
       n = read();
       rep(i,0,n-1) scanf("%lf%lf",&a[i].x,&a[i].y),dp[1<<i][i] = 1;
       rep(i,0,n-1)
       rep(j,0,n-1)
       rep(k,0,n-1)
       {
          if(k == i || k == j) continue;
          if(in(i,j,k))
          {
         //printf("%d %d %d
    ",i,j,k);
         if(a[k].x == a[i].x && a[k].x == a[j].x) G[i][j] |= (1<<k);
         else if(slope(i,j) == slope(k,j)) G[i][j] |= (1<<k);
          }
       }
       //rep(i,0,n-1)
          //{rep(j,0,n-1) printf("$%d ",G[i][j]);enter;}
       rep(i,1,(1<<n)-1)
       {
          rep(j,0,n-1)
          {
         if(!(i & (1 << j))) continue;
         rep(k,0,n-1)
         {
            if(i & (1<<k)) continue;
            if((i & G[j][k]) != G[j][k]) continue;
            dp[i | (1<<k)][k] = inc(dp[i][j],dp[i | (1<<k)][k]);
         }
          }
       }
       //rep(i,1,(1<<n)-1)
       //{rep(j,0,n-1) printf("#%d ",dp[i][j]);enter;}
       rep(i,1,(1<<n)-1)
       {
          if(count(i) < 4) continue;
          rep(j,0,n-1) if(i & (1 << j)) ans = inc(ans,dp[i][j]);
       }
       printf("%d
    ",ans);
       return 0;
    }
    

    T5.九连环
    传送门
    听说这题在数学书上有……?还是必修五?
    emm可能是A版……? 反正我的B版上没有TAT。
    然后通过爆搜通过百度知道,答案就是(lfloorfrac{2^n+1}{3} floor)
    然后就写一下FFT就能过…… 不过我太懒了 我用了python。 代码没有。

    T6.异或序列

    这个以前写过。看这里

  • 相关阅读:
    关键路径 图论 ——数据结构课程
    vue+flvjs实现flv格式视频流在线播放
    antdvue时间选择范围TimePicker 的使用,实现对应时间的禁用
    SVN(Subversion)中文站相关网址
    系统重启后ircdircu无法启动问题解决
    在Windows上安装Python+MySQL 的常见问题及解决方法
    windows 配置 pygraphviz
    C# 多线程操作TreeView
    ubuntu下解压rar文件乱码问题的解决
    将jar文件生成maven相关文件
  • 原文地址:https://www.cnblogs.com/captain1/p/10612041.html
Copyright © 2020-2023  润新知