10.29 考试总结

T1 birthday

Desprition

两种操作,第一种操作把 (l,r) 的区间的颜色赋为 (x),第二个操作询问 (l,r) 这一段区间不同颜色的个数。

(n,mleq 10^5,kleq 30) (k) 为颜色个数。

一开始每个点的颜色都为零

solution

mdzz考试的时候题面没说清楚,直接把我们机房一堆人都坑了。

本来打了正解,结果直接变成 (25) 分。

当时我的做法想的是对于每个叶子节点维护一个二进制数,(0/1) 表示这个颜色是否出现过。

(up) 的时候直接把左右儿子的二进制数或一下就行。

剩下的就是普通的区间覆盖和区间查询问题。线段树维护一下就可以。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N = 1e5+10;
int n,m,k,l,r,x,ans;
inline int read()
{
	int s = 0,w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
struct Tree
{
	int lc,rc;
	int sum,tag;
}tr[N<<2];
#define l(o) tr[o].lc
#define r(o) tr[o].rc
#define sum(o) tr[o].sum
#define tag(o) tr[o].tag
void up(int o)
{
	sum(o) = sum(o<<1) | sum(o<<1|1);
}
void cover(int o,int val)
{
	sum(o) = val;
	tag(o) = val;
}
void down(int o)
{
	if(tag(o))
	{
		cover(o<<1,tag(o));
		cover(o<<1|1,tag(o));
		tag(o) = 0;
	}
}
void build(int o,int L,int R)
{
	l(o) = L, r(o) = R;
	if(L == R)
	{
		sum(o) = 1;
		return;
	}
	int mid = (L + R)>>1;
	build(o<<1,L,mid);
	build(o<<1|1,mid+1,R);
	up(o);
}
void chenge(int o,int L,int R,int val)
{
	if(L <= l(o) && R >= r(o)) 
	{
		cover(o,val);
		return;
	}
	down(o);
	int mid = (l(o) + r(o))>>1;
	if(L <= mid) chenge(o<<1,L,R,val);
	if(R > mid) chenge(o<<1|1,L,R,val);
	up(o);
}
int query(int o,int L,int R)
{
	int res = 0;
	if(L <= l(o) && R >= r(o)) return sum(o);
	down(o);
	int mid = (l(o) + r(o))>>1;
	if(L <= mid) res |= query(o<<1,L,R);
	if(R > mid) res |= query(o<<1|1,L,R);
	return res;
}
int main()
{
//	freopen("birthday.in","r",stdin);
//	freopen("birthday.out","w",stdout);
	n = read(); k = read(); m = read();
	build(1,1,n);
	for(int i = 1; i <= m; i++)
	{
		char opt; cin>>opt;
		l = read(); r = read();
		if(l > r) swap(l,r);
		if(opt == 'C')
		{
			x = read();
			chenge(1,l,r,(1<<(x-1)));
		}
		else
		{
			int tmp = query(1,l,r); ans = 0;
			for(int j = 1; j <= k; j++) if(tmp & (1<<(j-1))) ans++;
			printf("%d
",ans);
		}
	}
	fclose(stdin); fclose(stdout);
	return 0;
}

T2 number

Desprition

给你一个数 (n) ,让你构造出一个数满足这个数只由 (4,7) 构成,同时 (4,7) 出现的次数相等。

请输出大于等于 (n) 的第一个满足条件的数。

(mleq 10^{100000},Tleq 100)

solution

正解是搜索,你敢信。

(1e5) 的数据范围你告诉搜索能过???,我直接亦或。

考试的时候打了个按位贪心,结果发现要考虑很多情况,直接傻了,调了半天没有调出来。

显然当 (n) 为奇数的时候 的 则答案长度必定为 (n+1) ,且是类似于 (44444...7777) 的形式。

对于 (n) 为偶数的时候,答案长度可能为 (n,n+2) ,但当长度为 (n+2) 的时候,也一定是 (44444...7777) 的形式。

然后,我们就可以搜索每一位上填 (4) 还是 (7) ,同时记录下还需要填的 (4,7) 的个数。

以及是否比原数大,如果你原数大的话,答案肯定是 先把剩下的 (4) 填完,在去填 (7).

一直搜下去就可以。

复杂度 (O(玄学))

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,four,seven,flag;
char ans[100010],ch[100010];
bool dfs(int x,int four,int seven,int flag)//four 记录剩下的 4的个数,seven记录剩下的7的个数,flag表示是否比原数大
{
	if(x >= n+1) return 1;
	if(flag)
	{
		for(int i = 0; i < four; i++) ans[x + i] = '4';
		for(int i = 0; i < seven; i++) ans[x+four+i] = '7';
		return 1;
	}
	if(ch[x] <= '4' && four && dfs(x+1,four-1,seven,ch[x] < '4')){ ans[x] = '4'; return 1;}//能填四就填四
	if(ch[x] <= '7' && seven && dfs(x+1,four,seven-1,ch[x] < '7')) { ans[x] = '7'; return 1;}//否则就填7
	return 0;
}
int main()
{
	freopen("number.in","r",stdin);
	freopen("number.out","w",stdout);
	while(scanf("%s",ch+1) != EOF)
	{
		n = strlen(ch+1);
		if(n % 2 || !dfs(1,n>>1,n>>1,0))
		{
			if(n %2 == 1) n++;
			else n += 2;
			for(int i = 1; i <= (n>>1); i++) ans[i] = '4';
			for(int i = (n>>1)+1; i <= n; i++) ans[i] = '7';
		}
		for(int i = 1; i <= n; i++) cout<<ans[i];
		printf("
");
	}
	fclose(stdin); fclose(stdout);
	return 0;
}

T4 肉

Desprition

两只狗在大街上发现了 (T) 大块肉。狐狸跑过来想帮它们分肉,可有了上次被坑的经历,两只狗拒绝它,选择自己分。对于块肉,甲狗和乙狗把肉一扯开,甲狗会得 (n) 千克的肉,乙狗得$ m$ 千克的肉,少肉的那条狗会抢对方的肉,使自己的肉多一倍。抢了 (k) 次后两条狗都累了,那么此时少肉的一方剩下多少千克的肉呢?(注:若两狗的肉一样多,甲狗会主动抢乙狗的肉)

对于30% 的数据 (kleq 10000)

对于60% 的数据 (n,mleq100000)

对于100% 的数据 (n,m,kleq 10^9)

sloution

阴间题。死也不会想到做法,看了题解一看就会的那种。

一个结论就是答案为 (min(n imes 2^k,m imes 2^k))

证明一下,每次操作 (n+m) 的总和是不变的,所以我们可以把所有的操作看成是在 模 (n+m) 的意义下进行的。

(n>m)。对于 (m) ,操作后变为 (m imes 2), (n) 则变为 (n-m)

然后考虑 (n-m = b) 怎么转化为 (n imes 2 mod (n+m) = b)

把模转化成 (ka+b) 的形式可以写成 (2 imes n = k(n+m) + b)

然后因为 (nleq n+m) 所以 (2 imes n leq 2 imes(n+m))

所以 (k) 的系数为 (1)

然后就转化为 (n-m -b = 2 imes n - (n+m) - b)

化简一下柿子可以写成 (n-m -b = n-m-b) ,发现等式两边其实是等价的。

所以每次操作可以看成 (n imes 2,m imes 2) ,然后快速幂处理一下就行。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
int T,n,m,k,p;
inline int read()
{
	int s = 0, w = 1; char ch = getchar();
	while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * w;
}
int ksm(int a,int b)
{
	int res = 1;
	for(; b; b >>= 1)
	{
		if(b & 1) res = res * a % p;
		a = a * a % p;
	}
	return res;
}
signed main()
{
	freopen("scramble.in","r",stdin);
	freopen("scramble.out","w",stdout);
	T = read();
	while(T--)
	{ 
		n = read(); m = read();  k = read();
		p = n+m;
		int base = ksm(2,k);
		printf("%lld
",min(n * base % p, m * base % p));
	}
	fclose(stdin); fclose(stdout);
	return 0;
}
}
原文地址:https://www.cnblogs.com/genshy/p/13899594.html