• 2017山东一轮集训 Day1


    2017山东一轮集训 Day1

    Sum

    题目描述:

    ​ 求有多少(n)位十进制数时(p)的倍数且每位之和小于等于$m_i (m_i = 0,1,2, dots , m - 1) $,允许前导零,答案对998244353取模。

    数据范围:

    $1 leqslant n leqslant 10^9 , 1 leqslant p leqslant 50 , 1 leqslant m leqslant 1000 $

    题解:

    ​ 位数和?前导零?(p)的倍数?数位dp裸题???不好意思(n)是十亿。。。怎么办?怎么办?由于可以忽略掉前导零,那么考虑倍增一下:

    [dp[i][j][k]:长度为i,数字和为j,\% p的余数为k的答案 \ dp[i][j][k] =sum_{x + y = j} sum_{(a+b) \% p = k} dp[ lfloor{ frac{i}{2} } floor ][x][a] * dp[ lfloor{ frac{i}{2} } floor ][y][b] ]

    ​ 对于一个长度(len)我们先把(lfloor frac{len}{2} floor)的答案即(dp[lfloor frac{len}{2} floor])求出来,这个直接递归算,然后先解决长度为偶数的情况。

    ​ 容易发现第一个和式是一个卷积的形式,那么我们将(dp)数组做一次(DFT),然后就可以愉快的直接暴力转移了。

    ​ 然后考虑长度为奇数的情况,其实就是少了一位(算出了(len - 1)的答案),那么只需要暴力枚举最高位即可。

    ​ 时间复杂度(O((p^2m+mlogm)log n))

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    const int MOD = 998244353;
    const int G = 3;
    const int maxn = 3010;
    
    inline int inc(int a, int b) { return (a + b >= MOD) ? (a + b - MOD) : (a + b); }
    inline int dec(int a, int b) { return (a > b) ? (a - b) : (a + MOD - b); }
    inline int mul(int a, int b) { return 1LL * a * b % MOD; }
    
    inline int power(int x, int k) {
      int tmp = 1;
      while(k) {
        if(k & 1) tmp = mul(tmp,x);
        x = mul(x,x);
        k >>= 1;
    	}
    	return tmp;
    }
    
    namespace NTT{
      int len, invlen;
      
      inline void init(int n) {
        len = 1;
        while(len <= n) len <<= 1; len <<= 1;
        invlen = power(len,MOD - 2);
    	}
    	
    	inline void rader(int *a, int n) {
    	  for(int i = 0, j = 0;i < n - 1;i ++) {
    	    if(i < j) swap(a[i],a[j]);
    	    int k = n >> 1;
    	    while(j >= k) {
    	      j -= k;
    	      k >>= 1;
    			}
    			if(j < k) j += k;
    		}
    	}
    	
    	inline void ntt(int *a, int kd) {
    		int n = len;
    	  rader(a,n);
    	  for(int i = 2;i <= n;i <<= 1) {
    	    int pn = power(G,(MOD - 1) / i);
    	    for(int j = 0;j < n;j += i) {
    	      int p = 1;
    	      for(int k = 0;k < (i >> 1);k ++) {
    	        int x = a[k + j], y = mul(a[k + j + (i >> 1)],p);
    	        a[k + j] = inc(x,y);
    	        a[k + j + (i >> 1)] = dec(x,y);
    	        p = mul(p,pn);
    				}
    			}
    		}
    	  if(kd == -1) {
    	    for(int i = 1;i < (n >> 1);i ++) swap(a[i],a[n - i]);
    	    for(int i = 0;i < n;i ++) a[i] = mul(a[i],invlen);
    	  }
      }
    }
    
    int n, p, m;
    int g[maxn][maxn], f[maxn][maxn];
    
    inline int work(int n) {
    	if(!n) return 1;
      int mi = work(n >> 1);
      for(int i = 0;i < p;i ++) NTT::ntt(g[i],1);
      for(int i = 0;i < p;i ++) {
        for(int j = 0;j < p;j ++) {
          int now = (i + 1LL * j * mi % p) % p;
          for(int k = 0;k < NTT::len;k ++) f[now][k] = inc(f[now][k],mul(g[i][k],g[j][k]));
    		}
    	}
    	for(int i = 0;i < p;i ++) NTT::ntt(f[i],-1);
    	for(int i = 0;i < p;i ++) {
    	  for(int j = 0;j < NTT::len;j ++) g[i][j] = 0;
    	  for(int j = 0;j <= m;j ++) g[i][j] = f[i][j];
    	  for(int j = 0;j < NTT::len;j ++) f[i][j] = 0;
    	}
    	mi = 1LL * mi * mi % p;
    	if(n & 1) {
    	  for(int i = 0;i < p;i ++) {
    	    for(int x = 0;x <= 9;x ++) {
    	      for(int j = 0;j + x <= m;j ++) {
    	        int now = (i + 1LL * x * mi % p) % p;
    	        f[now][j + x] = inc(f[now][j + x],g[i][j]);
    				}
    			}
    		}
    		for(int i = 0;i < p;i ++) {
    		  for(int j = 0;j <= m;j ++) g[i][j] = f[i][j];
    		  for(int j = 0;j < NTT::len;j ++) f[i][j] = 0;
    		}
    		mi = 1LL * mi * 10 % p;
    	}
    	return mi;
    }
    
    int main() {
    	scanf("%d%d%d", &n, &p, &m);
    	g[0][0] = 1; m ++;
    	NTT::init(m);
    	m --;
    	work(n);
    	int ans = 0;
    	for(int i = 0;i <= m;i ++) {
    		ans = inc(ans,g[0][i]);
    		printf("%d ", ans);
    	}
    	puts("");
      return 0;
    }
    

    Set

    题目描述:

    ​ 给出(n)个非负整数,将数划分成两个集合,记为一号集合和二号集合。(x_1)为一号集合中所有数的异或和,(x_2)为二号集合中所有数的异或和。在最大化(x_1+x_2)的前提下,最小化(x_1)

    数据范围:

    $1 leqslant n leqslant 10^5 $

    题解:

    ​ 异或!选数的子集!线性基!!!

    ​ 设(Sum)为所有数的异或和,那么题目变成最大化Sum^x1+x1,且最小化x1。按套路,从大到小贪心的决策,假设当前在第k位:

    ​ 如果Sum的第k位为1:那么x1的第k位最好为1,这样就有(2^{k - 1})的贡献

    ​ 如果Sum的第k位为0:那么x1的第k位最好为1,这样就有(2^k)的贡献

    考虑用线性基来维护这个贪心,容易发现按Sum的二进制建出两个线性基:Sum为1的位,Sum为0的位。这样优先级也就很容易确定了:Sum为0的优先于Sum为1的,高位优先于低位,直接贪心即可。

    时间复杂度:(O(n log N))

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    typedef long long ll;
    const int maxn = 100000 + 10;
    
    int n, vis[maxn];
    ll a[maxn], sum, s[70];
    
    inline void ins(ll x) {
      for(ll i = 62;i >= 0;i --) {
        if(!(sum & (1LL << i)) && (x & (1LL << i))) {
          if(!s[i]) {
            s[i] = x;
            return;
    			}
    			else x ^= s[i];
    		}
    	}
    	
    	for(ll i = 62;i >= 0;i --) {
    	  if((sum & (1LL << i)) && (x & (1LL << i))) {
    	    if(!s[i]) {
    	      s[i] = x;
    	      return;
    			}
    			else x ^= s[i];
    		}
    	}
    }
    
    int main() {
    	scanf("%d", &n); sum = 0;
    	for(int i = 1;i <= n;i ++) scanf("%lld", &a[i]), sum ^= a[i];
    	for(int i = 1;i <= n;i ++) ins(a[i]);
    	ll ans = 0;
    	for(ll i = 62;i >= 0;i --) {
    		if(!(sum & (1LL << i))) {
    		  ll tmp = (ans ^ s[i]) + (ans ^ s[i] ^ sum);
    		  ll now = ans + (ans ^ sum);
    		  if(tmp > now || (tmp == now && ans < (ans ^ s[i])))
    			  ans ^= s[i];
    		}
    	}
    	
    	for(ll i = 62;i >= 0;i --) {
    	  if(sum & (1LL << i)) {
    		  ll tmp = (ans ^ s[i]) + (ans ^ s[i] ^ sum);
    		  ll now = ans + (ans ^ sum);
    		  if(tmp > now || (tmp == now && ans < (ans ^ s[i]))) ans ^= s[i];
    		}
    	}
    	printf("%lld
    ", sum ^ ans);
      return 0;
    }
    

    Sim:咕咕咕

  • 相关阅读:
    jenkins 分布式部署
    Jenkins2.138配置slave节点时,启动方法只有两个选项
    SIFT特征详解
    OpenCV,计算两幅图像的单应矩阵
    OpenGL新手框架
    OpenGL超级宝典visual studio 2013开发环境配置,GLTools
    归一化变换 Normalizing transformations
    OpenCV2:特征匹配及其优化
    OpenCV2简单的特征匹配
    Qt自适应大小显示图片,添加菜单
  • 原文地址:https://www.cnblogs.com/ezhjw/p/9736685.html
Copyright © 2020-2023  润新知