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;
}
}