10.2模拟赛总结

10.2 模拟赛总结

T1.

数位dp:

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

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

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

\(f(i,j,k)\) 表示从低位数起的第 \(i\) 位,按照规则计算后答案为 \(j\quad (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\n");
		     else printf("NIE\n");
	}
	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]$

$ = v\sum\limits_{k|\gcd(\frac{x}{d},\frac{n}{d})} \mu(k)$ (日常反演)

$ = v\sum\limits_{k|\frac{x}{d},k|\frac{n}{d}} \mu(k)$

\(=\sum\limits_{k|\frac{n}{d},kd|x} v\mu(k)\)

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

可以等价于

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

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

考虑均摊复杂度

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

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

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

\(x\), 的前缀和就是

\(\sum\limits_{i=1}^x\sum\limits_{d|i} f(d)=\sum\limits_{d=1}^x f(d)\lfloor \frac{x}{d}\rfloor\)

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

时间复杂度$O(q\sqrt{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:\n", ++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\n", ans);
	     	}
		}	
	}
	return 0;
}

可以撒花了

然后这个柿子的理解

\(\sum\limits_{i=1}^x\sum\limits_{d|i}1=\sum\limits_{d=1}^{x}\lfloor\frac{x}{d}\rfloor\)

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

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

posted @ 2019-10-02 21:59  spbv587  阅读(109)  评论(0编辑  收藏  举报