zoukankan      html  css  js  c++  java
  • 10.2模拟赛总结

    10.2 模拟赛总结

    T1.

    数位dp:

    一个非常非常非常非常显然的数位 DP

    ([L,R] = [1,R]-[1,L-1])

    所以是分别求两次小于等于某个数字的方案数

    (f(i,j,k)) 表示从低位数起的第 (i) 位,按照规则计算后答案为 (jquad (j=0,1))

    (k) 表示只考虑后面结尾和 (lmt)后面几位 的大小关系 ((k=0,1))

    考虑第 (i+1) 位,算一下新构成的数字并判断下大小就可以了

    注意到 (L,R) 数据范围特别大,需要用高精度,最后结果要以二进制输出,所以可以对高精度压位

    (以上扒的题解)

    这题是个正常人就会想到找规律:

    然后就有打表:(1~100)

    然后就没了(啥?还有高精呢)

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #define LL long long
    using namespace std;
    int n, q, v,  t, L, R, len;
    char s[208];
    struct bigint
    {
    	int len, zz;
    	int v[1005];
    	bigint(){len = 0; memset(v, 0, sizeof v); zz = 1;}
    	bigint(int x)
    	{
    		if(x >= 0) zz = 1;
    		else x = -x, zz = 0;
    		len = 0;
    		memset(v, 0, sizeof v);
    		while(x)
    		{
    			v[ ++len] = x % 10;
    			x /= 10;
    		}
    	}
    	friend bool operator < (const bigint &a, const bigint &b)
    	{
    		if(a.len < b.len) return 1;
    		if(a.len > b.len) return 0;
    		for(int i = a.len ; i >= 1; i -- )
    		{
    			if(a.v[i] < b.v[i]) return 1;
    			if(a.v[i] > b.v[i]) return 0;
    		}
    		return 0;
    	}
    	friend bool operator == (const bigint &a, const bigint &b)
    	{
    		if(a.len != b.len ) return 0;
    		for(int i = a.len; i >= 1; i --)
    		{
    			if(a.v[i] != b.v[i]) return 0;
    		}
    		return 1;
    	}
    	friend bool operator <= (const bigint &a, const bigint &b)
    	{
    		if(a < b) return 1;
    		else if(a == b) return 1;
    		else return 0;
    	}
    	friend bool operator != (const bigint &a, const bigint &b)
    	{
    		if(a.len != b.len) return 1;
    		for(int i = a.len; i >= 1; i --)
    		{
    			if(a.v[i] != b.v[i]) return 1;
    		}
    		return 0;
    	}
    }x, y, res;
    bigint operator + (bigint a, bigint b)
    {
    	int len = a.len + b.len;
    	bigint c;
    	c.len = len;
    	for(int i = 1; i <= len; i ++)
    	 c.v[i] = a.v[i] + b.v[i];
    	for(int i = 1; i <= len; i ++)
    	{
    		if(c.v[i] >= 10)
    		{
    			++c.v[i+1];
    			c.v[i] -= 10;
    		}
    	}
    	while(c.len&&!c.v[c.len]) c.len --;
    	return c;
    }
    bigint operator - (bigint a, bigint b)
    {
    	int len = max(a.len, b.len);
    	bigint c;
    	for(int i = 1; i <= len; i ++)
    	c.v[i] = a.v[i] - b.v[i];
    	c.len = len;
    	for(int i = 1; i <= c.len; i ++)
    	{
    		if(c.v[i] < 0)
    		{
    			c.v[i+1]--;
    			c.v[i] += 10;
    		}
    	}
    	while(c.len&&!c.v[c.len]) c.len --;
    	return c;
    }
    bigint operator *(bigint a,bigint b)
    {
        bigint c;
        for(int i = 1; i <= a.len; ++ i)
        for(int j = 1; j <= b.len; ++ j)
        c.v[i+j-1] += a.v[i] * b.v[j];
        c.len = a.len + b.len;
        for(int i = 1;  i <= c.len - 1; ++ i)
        {
            if(c.v[i] >= 10)
            {
                c.v[i+1] += c.v[i] / 10;
                c.v[i] %= 10;
            }
        }
        while(c.v[c.len] == 0&&c.len > 1) -- c.len;
        return c;
    }
    bigint operator /(bigint a,long long b)
    {
        bigint c;int d = 0;
        for(int i = a.len; i >= 1; -- i)
        c.v[++ c.len] = ((d * 10 + a.v[i]) / b),d=(d*10+a.v[i])%b;
        for(int i=1;i<=c.len/2;++i)swap(c.v[i],c.v[c.len-i+1]);
        while(c.v[c.len]==0&&c.len>1)--c.len;
        return c;
    }
    bigint Min(bigint a, bigint b)
    {
    	if(a < b) return a;
    	else return b;
    }
    bigint work(bigint x)
    {
    	if(x < bigint(4)) return bigint(1);
    	bigint l = bigint(4), r = Min(x, bigint(7)), res = bigint(1);
    	int opt = 1;
    	for(; ; l = r + bigint(1), r = Min(r * bigint(2)  + bigint(1), x), opt ^= 1 )
    	{
    		if(opt)
    		  res = res + (r - l + bigint(1));
    		  if(r == x) break;
    	}
    	return res;
    }
    void out(bigint x)
    {
    	if(!x.len) return (void)printf("0");
    	bigint qwq = bigint(1);
    	while(qwq <= x) qwq = qwq * bigint(2);
    	qwq = qwq / 2;
    	for(; ; qwq = qwq /2)
    	{
    		if(qwq <= x)
    		{
    			printf("1");
    			x = x - qwq;
    		}
    		else printf("0");
    		if(qwq == bigint(1))break;
    	} 	
    }
    void solve()
    {
    	x = y = res = bigint(0);
    	scanf("%s", s + 1);
    	for(int i = 1; i <= n; i ++)
    	{
    		x = x * 2 + bigint(s[i] - '0');
    	}
    	scanf("%s", s + 1);
    	for(int i = 1; i <= n; i ++)
    	{
    		y = y * 2 + bigint(s[i] - '0');
    	}
    	res = work(y) - work(x - bigint(1));
    	if((n&1) == (q&1)) res = y - x + 1 - res;
    	out(res);
    	puts("");
    }
    signed main()
    {
    //	freopen("a.in", "r", stdin);
    //	freopen("a.out", "w", stdout);
    	scanf("%d", &t);
    	while(t --)
    	{
    		scanf("%d%d",&n, &q);
    		solve();
    	}
    	return 0;
    }
    

    T2

    一sb题, 没啥总结的。。

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #define LL long long
    #define N 100005
    using namespace std;
    struct node
    {
    	int w1, w2, l1, l2;
    }q[N];
    int n, m, t, minw = 2e9, maxw = -233, minl = 2e9, maxl = -233, flag; 
    signed main()
    {
    	freopen("b.in", "r",stdin);
    	freopen("b.out", "w", stdout);
    	scanf("%d", &t);
    	while(t -- )
    	{
    		flag = 0; minw = minl = 2e9; maxw = maxl = -233;
    		scanf("%d", &n);
    		for(int i = 1; i <= n; i ++)
    			scanf("%d%d%d%d", &q[i].w1, &q[i].w2, &q[i].l1, &q[i].l2);
    		for(int i = 1; i <= n; i ++)
    		{
    			minw = min(minw, q[i].w1);
    			maxw = max(maxw, q[i].w2);
    			minl = min(minl, q[i].l1);
    			maxl = max(maxl, q[i].l2);
    		}
    		for(int i = 1; i <= n; i ++)
    		{
    			if(q[i].w1 <= minw&&q[i].w2 >= maxw&&q[i].l1 <= minl&&q[i].l2 >= maxl)
    			{flag = 1; break; }
    		}
    		if(flag)printf("TAK
    ");
    		     else printf("NIE
    ");
    	}
    	return 0;
    }
    

    T3

    毒瘤数据结构+数论题

    --给定1, n, d, v 给序列所有满足(gcd(x, n)=d)(x), 给(a[x]+=v);

    就相当于(a[x]+=v[gcd(x, n)==d])

    然后就可以愉快的推式子了

    ( v[gcd(x,n) = d])

    $ = v [gcd(frac{x}{d},frac{n}{d})=1]$

    $ = vsumlimits_{k|gcd(frac{x}{d},frac{n}{d})} mu(k)$ (日常反演)

    $ = vsumlimits_{k|frac{x}{d},k|frac{n}{d}} mu(k)$

    (=sumlimits_{k|frac{n}{d},kd|x} vmu(k))

    暴力做法显然是要枚举(x), 对于每一个({k|frac{n}{d}且kd|x}), 都加上(vmu(k)),

    可以等价于

    对于一个合法的(k|dfrac{n}{d}), 则(x =kd,2kd,3kd...), 枚举(k), 把所有(kd), 的倍数都加上(vmu(k));

    这样虽然(O(1))查询, 但修改的复杂度太大

    考虑均摊复杂度

    我们开一个数组(f) 表示所有是(i), 的倍数的位置都加上(f[i])

    修改时只需找出合法的(k), 然后(f[kd]+=vmu(k)), 省去了枚举(kd) 的倍数;

    然后查询时 查询一个数(i) 时, 就成了(sum_{d|i}f(d))

    (x), 的前缀和就是

    (sumlimits_{i=1}^xsumlimits_{d|i} f(d)=sumlimits_{d=1}^x f(d)lfloor frac{x}{d} floor)

    然后就可以整除分块, 对与每一块需要求出那一块的(f)的和;单点修改区间求和树状数组可以维护;

    时间复杂度$O(qsqrt{l}log l+ l log l) $

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <cstdio>
    #define int long long
    #define N 200005
    using namespace std;
    int val[N], a[N], l, m, tot;
    void add(int pos, int v)
    {
    	for(int i = pos; i <= l; i += i&(-i))
    	    val[i] += v;
    }
    int ask(int pos)
    {
    	int res = 0;
    	for(int i = pos; i ; i -= i&(-i))
    	   res += val[i];
    	return res;
    }
    int mu[N], prime[N], vis[N];
    void yych()
    {
    	mu[1] = 1;
    	for(int i = 2;i <= N; i ++)
    	{
    		if(!vis[i])
    		{
    			mu[i] = -1;
    			prime[++tot] = i;
    		}
    		for(int j = 1; j <= tot&& i * prime[j]<= N; j ++)
    		{
    			vis[i * prime[j]] = 1;
    			if(i%prime[j] == 0)
    			{
    				mu[i * prime[j]] = 0;
    				break;
    			}
    			mu[i*prime[j]] = -mu[i];
    		}
    	}
    }
    signed main()
    {
    	freopen("c.in", "r",stdin);
    	freopen("c.out", "w", stdout);
        int cas = 0, opt, x, y, z, n, d, v;
        yych();
    	while(scanf("%lld%lld", &l, &m) && l && m)
    	{
    		for(int i = 1; i <= l; i ++) val[i] = 0;
    		printf("Case #%lld:
    ", ++cas);
    		for(int i = 1; i <= m ;i ++)
    		{
    			scanf("%lld",&opt);
    	    	if(opt&1)
    	    	{
    		     	scanf("%lld%lld%lld", &n, &d, &v);
    		     	if(n%d!=0)continue;
    		     	int q = n/d;
    		    	for(int p = 1; p * p <= q; p ++)
    		    	{
    			    	if(q %  p == 0) 
    			    	{
    			    		add(d*p, v * mu[p]);
    				    	if(p * p != q)
    				    	add(d*(q/p), v * mu[q/p]);  
    			    	}
    		    	}	
    	    	}
    	    	else
    	     	{
    		     	scanf("%lld", &x);
    			    int ans = 0;
    	    		for(int l = 1, r; l <= x; l = r + 1)
    	    		{
    		    		r = min(x / (x / l), x);
    			    	ans += (x/l) * (ask(r) - ask(l - 1));
    		    	}
    		    	printf("%lld
    ", ans);
    	     	}
    		}	
    	}
    	return 0;
    }
    

    可以撒花了

    然后这个柿子的理解

    (sumlimits_{i=1}^xsumlimits_{d|i}1=sumlimits_{d=1}^{x}lfloorfrac{x}{d} floor)

    1到x每一个数的所有约数的个数

    就相当于枚举一个约数, 这个约数的倍数的个数, x以内d的倍数的个数就是(lfloorfrac{x}{d} floor);

  • 相关阅读:
    名字对战,看看你名字里蕴藏的力量
    趣味小游戏,测试你们做过的傻事
    贪吃蛇小游戏的初步尝试制作
    实现成绩表的初步想法
    结构体的一些小知识点
    慢慢完善-小游戏之推箱子
    よんにち
    さんか表单 框架 样式表和样式表选择器
    Two Day
    One day
  • 原文地址:https://www.cnblogs.com/spbv587/p/11618615.html
Copyright © 2011-2022 走看看