A. Typical Interview Problem(思维)
题意:
给定一个由字符"F"和/或"B"组成的字符串s。我们需要确定这个字符串是否是FB字符串的子串,即是否存在两个整数l和r(1≤l≤r),使得字符串flfl+1fl+2…fr正好等于s。
FB字符串的构造方式如下。初始时,它是一个空字符串。我们按照从1开始的升序遍历所有的正整数,并对每个整数执行以下操作:如果当前整数能被3整除,则在FB字符串的末尾添加字符"F";
如果当前整数能被5整除,则在FB字符串的末尾添加字符"B"。
需要注意的是,如果一个整数既能被3整除又能被5整除,则先添加字符"F",然后添加字符"B",而不是反过来。
FB字符串是无限长的。它的前10个字符是FBFFBFFBFB:第一个"F"来自整数3,下一个字符"B"来自整数5,下一个"F"来自整数6,依此类推。很容易看出,这个字符串是无限长的。
现在,我们需要判断给定的字符串s是否是FB字符串的子串。也就是说,我们需要确定是否存在两个整数l和r,使得字符串flfl+1fl+2…fr恰好等于s。
例如:字符串FFB是FB字符串的子串:如果我们选择l=3和r=5,那么字符串f3f4f5恰好等于FFB;
字符串BFFBFFBF是FB字符串的子串:如果我们选择l=2和r=9,那么字符串f2f3f4…f9恰好等于BFFBFFBF;
字符串BBB不是FB字符串的子串。
思路:
前五分钟没想出什么思路,列一下就懂了,出现的数字分别是3 5 9 15 18 20
后面是20+3 20+5 20+9 20+15 20+18 20+20,循环了
所以直接找有无重复,无重复就是没有
#include<bits/stdc++.h>
using namespace std;
int main()
{
string cmp;
for(int i=1;i<=500;i++)
{
if(i%3==0)cmp+='F';
if(i%5==0)cmp+='B';
}
//cout<<cmp<<endl;
int t;
cin>>t;
while(t--)
{
string kk,num;
cin>>num>>kk;
int len = kk.length();
if(cmp.find(kk)!=-1)cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
B. Asterisk-Minor Template(思维)
题意:
给定两个字符串a和b,它们由小写拉丁字母组成。
模板t是一个由小写拉丁字母和星号(字符’*')组成的字符串。如果模板中的星号数量小于或等于字母数量,那么这个模板被称为星号次要模板。
如果你可以用小写拉丁字母(可能为空字符串)替换模板t中的每个星号,使得它变成与字符串s相等,则称字符串s与模板t匹配。
找到一个星号次要模板,使得字符串a和b都与它匹配,或者报告这样的模板不存在。如果有多个答案,则可以任选一个打印出来
思路:
如果匹配,那么*的长度最大为2即可。
判断开头结尾是否相同
判断是否有连续的字母相连即可
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
string b;
cin>>a>>b;
int lena = a.length(),lenb = b.length();
if(a[0]==b[0])
{
cout<<"YES"<<endl;
cout<<a[0]<<"*"<<endl;
continue;
}
if(a[lena-1]==b[lenb-1])
{
cout<<"YES"<<endl;
cout<<"*"<<a[lena-1]<<endl;
continue;
}
int fl = 1;
for(int i=0;i<lena-1;i++)
{
string cmp;
cmp+=a[i];
cmp+=a[i+1];
if(b.find(cmp)!=-1)
{
cout<<"YES"<<endl;
cout<<"*"<<cmp<<"*"<<endl;
fl = 0;
break;
}
}
if(fl)
cout<<"NO"<<endl;
}
return 0;
}
C. Maximum Set (简单数论)
题意:
给定两个整数 l 和 r,定义一个正整数集合 S 为美丽的,如果对于集合中的任意两个整数 x 和 y,x 整除 y 或者 y 整除
x(或者两者都满足)。 你需要考虑所有由不小于 l 且不大于 r 的整数组成的美丽集合。你需要打印两个数: 在所有元素都来自于 l 到 r
的美丽集合中,最大可能的美丽集合的大小; 由 l 到 r 的整数组成的美丽集合中,具有最大可能大小的美丽集合的数量。
由于第二个数可能非常大,你需要对其进行模运算,模数为 998244353。
思路:
若使得距离最长,那么就要求每次前进最短,那么我们每次乘以2就好了…
我只想到这一层,后面看了题解
发现在一堆乘以2的数字中,比如序列:
4 * 2 ,4 * 2 * 2, 4 * 2 * 2 * 2 , 4* 2 *2 * 2 *2…
可以变为
4 * 2,4 * 3 * 2 , 4 * 3 * 2 * 2…这样会使得长度相同,但结尾变大了,使得整个串也不相同了
我们就需要二分这个3最大可以出现在哪个地方即可。
#include<bits/stdc++.h>
using namespace std;
#define LL long long
long long q_pow(long long a, long long n)
{
if (n == 0)
return 1;
else if (n % 2 == 1)
return q_pow(a, n - 1) * a;
else
{
int temp = q_pow(a, n / 2);
return temp * temp;
}
}
//(ans+mod)%mod可以防止负数
int main()
{
long long t;
cin>>t;
while(t--)
{
long long ans = 0;
long long l,r;
scanf("%lld%lld",&l,&r);
long long begin = l;
long long len = 0;
while(begin<=r)
{
len++;
begin*=2;
}
cout<<len<<' ';
//找2,找3
long long ll = l-1 ,rr = r+1;
while(ll<rr)
{
long long mid = (ll+rr+1)/2;
//cout<<"mid"<<mid<<endl;
//cout<<mid*q_pow(2,len-1)<<endl;
if(mid*q_pow(2,len-1)<=r)//找到最大的且小与r的数
{
ll = mid;
}
else
{
rr = mid-1;
}
}
//cout<<ll<<endl;
if(ll>=l)
ans+=ll-l+1;
ll = l-1;rr = r+1;
while(ll<rr)
{
long long mid = (ll+rr+1)/2;
//cout<<"mid"<<mid<<endl;
//cout<<mid*q_pow(2,len-1)<<endl;
if(mid*q_pow(2,len-2)*3<=r)//找到最大的且小与r的数
{
ll = mid;
}
else
{
rr = mid - 1;
}
}
//cout<<ll<<endl;
if(ll!=l-1)
ans += (ll - l + 1) * (len - 1);
cout<<ans<<endl;
//cout<<ll<<"?"<<endl;
}
}
D. Maximum Subarray (DP)
题意:
给定一个由 n 个整数组成的数组 a1,a2,…,an,以及两个整数 k 和 x。 你需要执行以下操作:将 x 添加到恰好 k
个不同位置的元素上,并从其它元素中减去 x。每个元素只能选择一次。 例如,如果
a=[2,-1,2,3],k=1,x=2,并且我们选择了第一个元素,则操作后的数组为 a=[4,-3,0,1]。 定义 f(a) 为数组 a
的子数组的最大可能和。子数组是数组 a 的连续部分,即数组 ai,ai+1,…,aj,其中
1≤i≤j≤n。空的子数组应该被考虑在内,其和为 0。 令数组 a’ 是应用上述操作后的数组 a。以使 f(a’)
的值最大化的方式应用操作,并打印 f(a’) 的最大可能值。
思路:
没有想到要差分,想思路的过程太折磨了。。。。
当x大于0的时候,我们只需要找出区间长度为k的最大前缀和+k*x即可。
当x小于0的时候,这时候相当于选择n-k个点去加上x,找出长度为n-k的最大前缀和。
当k为0的时候,需要找的是长度为1到长度为len的最大前缀和,但当n=1e5的时候,这个复杂度太高了…想不出来其他方案,随后看题解
结果是dp…
f[i][j] 表示的是按照顺序选到第i个数字的时候,增加了j个的连续区间的最大值。
所以有
1.当i==j的时候,前面全部都加了,自己必须也加x。
2.定义合法范围。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10, M = 30;
int a[N];
LL f[N][M];
void solve() {
int n, m, k;
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
f[i][j] = -0x3f3f3f3f3f3f3f3f;
}
}
f[0][0] = 0;
LL ret = 0;
for (int i = 1; i <= n; i++) {
for (int j = max(0, m - n + i); j <= m && j <= i; j++) {
if (j < i) f[i][j] = max(0ll, f[i - 1][j]) + a[i] - k;
if (j) f[i][j] = max(f[i][j], max(0ll, f[i - 1][j - 1]) + a[i] + k);
ret = max(ret, f[i][j]);
}
}
printf("%lld\n", ret);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
A. Two Towers (签到题)
题意:
有两座由红色和蓝色方块组成的塔楼。这两座塔楼由字符B和/或R表示,表示从底部到顶部的方块顺序,其中B表示蓝色方块,R表示红色方块。
这两座塔楼由字符串BRBB和RBR表示。 你可以执行以下操作任意次数:选择一座至少有两个方块的塔楼,将其顶部方块移到另一座塔楼的顶部。
如果没有相邻的方块颜色相同(即没有红色方块在另一个红色方块上方,也没有蓝色方块在另一个蓝色方块上方),则这对塔楼是美丽的。
你需要判断是否可以执行任意次数的操作(可能为零次),使得这对塔楼变得美丽。
思路:
模拟了一下过程,发现拼接是有顺序的,要么a拼接翻转b,要么b拼接翻转a
拼接完毕后设断点,如果有两个连续的BB或者RR,断点放在任何一个地方都不行。
如果有3个或者3个以上的的连续字串,怎么样都不可以
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
string a;
string b;
string ra;
string rb;
int n1,n2;
cin>>n1>>n2>>a>>b;
int lena = a.length();
int lenb = b.length();
for(int i=lena-1;i>=0;i--)
{
ra+=a[i];
}
for(int i=lenb-1;i>=0;i--)
{
rb+=b[i];
}
string des1;
string des2;
des1 = a+rb;
des2 = b+ra;
int sum = 0;
int len1 = des1.length();
int len2 = des2.length();
int fl = 0;
for(int i=0;i<len1;i++)
{
//连续字串
int len = 1;
while(des1[i]==des1[i+1])
{
i++;
len++;
}
if(len==2)
{
sum++;
}
if(len>=3)
{
cout<<"NO"<<endl;
fl = 1;
break;
}
//cout<<len<<' ';
}
if(fl)continue;
//<<des1<<endl;
if(sum<=1)
{
cout<<"YES"<<endl;
continue;
}
sum = 0;
for(int i=0;i<len2;i++)
{
//连续字串
int len = 1;
while(des2[i]==des2[i+1])
{
i++;
len++;
}
if(len>=2)
{
sum++;
}
if(len>2)
{
fl = 1;
cout<<"NO"<<endl;
}
}
if(fl)continue;
if(sum<=1)
{
cout<<"YES"<<endl;
continue;
}
cout<<"NO"<<endl;
}
return 0;
}
B . Ideal Point(差分)
题意:
你被给定一个由n个整数组成的数组a1,a2,…,an。同时给定两个整数k和x。
你需要执行以下操作:恰好一次地,在k个不同的位置上加上x,并从其他所有元素中减去x。
例如,如果a=[2,-1,2,3],k=1,x=2,并且我们选择了第一个元素进行操作,那么操作后的数组a=[4,-3,0,1]。
定义f(a)为数组a的子数组的最大可能和。子数组是指数组a的连续一部分,即对于某些1≤i≤j≤n,数组ai,ai+1,…,aj。空子数组也应该被考虑在内,它的和为0。
令数组a’为应用上述操作后的数组a。请以使f(a’)的值最大化的方式执行操作,并输出f(a’)的最大可能值。
思路:
想了一下可能性,当所有线段都关于他的时候,他是最大的,或者只有和他相等的,这时无论减少多少条线段,他都会减一,但是可能和他相等的数不会减一。所以不如我们直接只选择这个x所在的区间,进行差分,最终看结果是否只有他最大,如果有和他相等的,输出NO
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5;
int cf[N];
int a[N];
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
int maxr = 0;
for(int i=1;i<=n;i++)
{
int l,r;
cin>>l>>r;
if(k>=l && k<=r)
{
cf[l]+=1;
cf[r+1]-=1;
maxr = max(maxr,r);
}
}
int rk = 0;
if(maxr==0)
{
cout<<"NO"<<endl;
continue;
}
//cout<<maxr<<endl;
for(int i=1;i<=maxr;i++)
{
rk += cf[i];
a[i] = rk;
}
//cout<<endl;
int des = a[k];
int fl = 1;
//cout<<des<<endl;
for(int i=1;i<=maxr;i++)
{
if(i==k)continue;
if(a[i]==des)
{
cout<<"NO"<<endl;
fl = 0;
break;
}
}
if(fl)cout<<"YES"<<endl;
memset(cf,0,sizeof cf);
}
return 0;
}
C. Tea Tasting (二分 前缀和 差分)
题意:
一个茶叶生产商决定进行一次大规模的品茶活动。n种茶叶将由n个品茶师品尝。茶叶和品茶师都从1到n进行编号。生产商准备了ai毫升的第i种茶叶。第j个品茶师一次可以喝bj毫升的茶。
品茶活动将分步进行。在第一步中,第i个品茶师品尝第i种茶叶。第i个品茶师喝下min(ai,bi)毫升的茶(取决于第i种茶叶的剩余量和第i个品茶师一次可以喝的量)。ai也会相应减少这个数量。
然后所有的品茶师都移动到上一种茶叶。因此,在第二步中,第i个品茶师品尝第(i-1)种茶叶。第i个品茶师喝下min(ai-1,bi)毫升的茶。第1个人结束品茶活动。
在第三步中,第i个品茶师品尝第(i-2)种茶叶。第2个品茶师结束品茶活动。这一过程一直持续到所有人结束品茶活动。
以n=3,a=[10,20,15],b=[9,8,6]为例,左列显示了每种茶叶的当前数量,右列显示了每个品茶师总共喝了多少茶。箭头表示茶叶在当前步骤中分给哪个品茶师。箭头上的数字是茶叶的数量,取决于茶叶的剩余量和品茶师一次可以喝的量的最小值。
思路:
想到差分 想到前缀和 想到第i个茶哥只能喝到i到n杯茶。又想到在不限制茶叶的情况下,茶叶会被消耗多少,茶叶量可以透支,变为负数,当他变为负数的时候,我们只需要让喝茶兄弟背债即可,后面把他所喝的茶叶减去所需还债量就是他喝的多少,但是复杂度很高,不通过。
看了题解:可以这样想,每个茶叶最多供应多少杯茶叶,当到第i个人品茶的时候,就不能取了。用前缀和,二分实现。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
const int N = 2e5 + 10;
int T;
int a[N], b[N], cf[N], add[N], pre[N];
int find(int x, int tmp, int nn)
{
int l = tmp + 1, r = nn;
while(l < r)
{
int mid = l + r + 1>> 1;
if(pre[mid] - pre[tmp] <= x) l = mid;
else r = mid - 1;
}
return r;
}
signed main()
{
scanf("%lld", &T);
int n;
while(T--)
{
scanf("%lld", &n);
for (int i = 1; i <= n; ++ i)
{
scanf("%lld", &a[i]);
pre[i] = 0, cf[i] = 0, add[i] = 0;
}
for (int i = 1; i <= n; ++ i) scanf("%lld", &b[i]), pre[i] = pre[i - 1] + b[i];
int k = 0;
add[n] = min(b[n], a[n]);
for (int i = 1; i < n; ++ i)
{
int ci = find(a[i], k, n);
if(b[i] > a[i]) {
add[i] += a[i];
k++;
continue;
}
int now = ci - k;
k ++;
cf[k] ++, cf[k + now] --;
if(pre[ci] - pre[k - 1] < a[i]) add[k + now] += a[i] - pre[ci] + pre[k - 1];
}
for (int i = 1; i <= n; ++ i)
{
cf[i] += cf[i - 1];
printf("%lld ", cf[i] * b[i] + add[i]);
}
puts("");
}
return 0;
}
D Triangle Coloring(数论)
题意:
给定一个由n个顶点和n条边组成的无向图,其中n是6的倍数。每条边的权重是一个正整数。
图的结构如下:它被分为n/3个顶点三元组,第一个三元组由顶点1、2、3组成,第二个三元组由顶点4、5、6组成,依此类推。同一个三元组中的任意两个顶点之间都有一条边相连,不同三元组之间没有边相连。
你需要将图的顶点涂成两种颜色,红色和蓝色。每个顶点必须有且只有一种颜色,其中红色顶点的数量应为n/2,蓝色顶点的数量也应为n/2。满足这些约束条件的着色被称为有效着色。
着色的权重是连接两个不同颜色顶点的边的权重之和。
设W为有效着色的最大可能权重。计算具有权重W的有效着色的数量,并以模998244353的形式打印出来。
思路:
没看懂题目一开始,以为是图论,后面慢慢读就懂了。。。
题解:
设当前遍历的区间中的三个数为a b c
1.abc 是一样的:那么随便染色,三种情况
2.存在ab>c 那么有两种不同的染色选择
3.存在bc<a,那么有两种不同染色选择
4.存在三个不一样的,那么只有一两种情况
因为存在p为质数,m和n过大,所以可以用卢卡斯定理算组合数
用乘法原理+卢卡斯定理(内容来自B站董晓算法)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 998244353;
const int N = 3e5 + 10;
ll qmi(ll a,ll b,ll p)
{
ll res = 1;
while(b)
{
if(b & 1)res = res % p * a % p;
a = a % p * a % p;
b >>= 1;
}
return res;
}//快速幂
ll c(ll a,ll b, ll p)//卢卡斯定理求组合数
{
if(b > a)return 0;
ll res = 1;
for(int i = 1,j = a;i <= b; i++,j--)
{
res = res % p * j % p;
res = res % p * qmi(i, p-2, p) % p;
}
return res;
}//卢卡斯
ll lucas(ll a,ll b,ll p)
{
if(a < p && b < p)return c(a,b,p);
else return c( a % p,b % p, p) * lucas(a / p, b / p, p ) % p;
}
int v[N];
void solve()
{
int n;
cin >> n;
int cnt = 0;
ll res = 1;
int a,b,c;
for(int i = 0; i < n/3; i ++ )
{
cin>>a>>b>>c;
cnt = 0;
int maxm = 0;
maxm = max(a, max(b, c));
if(a != b && b != c && c != a)//全都不相等,只有一种可能
res *= 1;
else if( a == b && b == c && a == c)//三个相等
res = (res * 3) % mod;
else
{
multiset<int> ma;
ma.insert(a);
ma.insert(b);
ma.insert(c);
if(ma.count(maxm) != 2)//两个大于一个 就乘以2,否则只有一种选择
res = (res * 2) % mod;
}
}
cout << ( res * lucas(n / 3, n / 6, mod) ) % mod << endl; // 总数量再乘以染色数(C(n/3,n/6));公有n/3组,从这些组选出一半染色
}
int main()
{
solve();
}