算法板块
我们用一段的终点来划分区间,dp[i]表示的是前i头牛的最大效率,首先考虑第i头牛选不选,若不选则dp[i] = dp[i - 1],若选则需要考虑上一个断点在哪个位置,上一个断点的位置在[i - k + 1 ~ i - 1]的位置,因此想到单调队列优化,因为是区间和因此还需要用到前缀和优化,若选择第i头牛则dp[i] = dp[i - x - 1] + a[i] - a[i - x],也就是要找到dp[i - x - 1] - a[i - x]的最大值,我们把这一项用滑动窗口进行维护即可.
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
const ll inf = 1e18;
int n,k;
ll a[N],dp[N];
ll q[N];
ll g(int x)
{
if(x == 0)
{
return 0;
}else
{
return dp[x - 1] - a[x];
}
}
int main()
{
cin>>n>>k;
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
a[i] += a[i - 1];
}
int tt = -1,hh = 0;
q[++tt] = 0;
for(int i = 1 ; i <= n ; i++)
{
while(hh <= tt && q[hh] < i - k)
{
hh++;
}
while(hh <= tt && g(q[tt]) <= g(i))
{
tt--;
}
dp[i] = max(dp[i - 1] , a[i] + g(q[hh]));
q[++tt] = i;
}
cout<<dp[n];
}
今天打了一下牛客小白月赛,只ac了3题,属于是屎中屎了,赛后5min出了d,并不难,明天有机会把e也给补了
1.A-tb的区间问题_牛客小白月赛101 (nowcoder.com)
一开始的思路是k次循环,每次比较首元素和尾元素,谁小删哪个,这是一个错误的贪心思路,例如k = 2的时候数组是8 7 1 8或者8 1 7 8总有一个能卡住这个思路,所以不妨采取逆向思维,我们看保留的n - k个数,一定是一个连续的n - k个数,那么就找sum最大的连续的n - k个数即可。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e5 + 10,inf = 0x3f3f3f3f,mod = 998244353;
int n,k;
ll a[N];
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>k;
ll maxn = -1;
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
a[i] += a[i - 1];
}
for(int i = 1 ; i + n - k <= n ; i++)
{
maxn = max(maxn , a[i + n - k] - a[i]);
}
cout<<maxn;
}
2.B-tb的字符串问题_牛客小白月赛101 (nowcoder.com)
类似于括号匹配的问题,那么就是用栈来维护,当遇到f或者t的时候就入栈,遇到c或者b的时候就看是否能与栈顶匹配,若能匹配两两相消,否则清空栈,若是其他字符也是清空
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e5 + 10,inf = 0x3f3f3f3f,mod = 998244353;
int n;
string s;
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>s;
stack <char> stk;
int len = 0;
for(int i = 0 ; i < n ; i++)
{
if(s[i] == 'f')
{
stk.push('f');
}else if(s[i] == 't')
{
stk.push('t');
}else if(s[i] == 'c' && stk.size())
{
if(stk.top() == 'f')
{
stk.pop();
len += 2;
}else
{
stack <char>().swap(stk);
}
}else if(s[i] == 'b' && stk.size())
{
if(stk.top() == 't')
{
stk.pop();
len += 2;
}else
{
stack <char>().swap(stk);
}
}else
{
stack <char>().swap(stk);
}
}
cout<<n - len;
}
3.C-tb的路径问题_牛客小白月赛101 (nowcoder.com)
输出几张地图可以发现,当是偶数的时候gcd(n - 2 , n) == 2,对角线上的元素是i,那么偶数的时候4步就能走到,奇数的时候可以由偶数转移,通过4步走到n - 1后再走两步到n。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e6 + 10,inf = 0x3f3f3f3f,mod = 998244353;
int n;
int ans[N];
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
ans[1] = 0;
ans[2] = 2;
for(int i = 3 ; i <= n ; i++)
{
if(__gcd(i - 2 , i) == 2)
{
ans[i] = 4;
}else
{
ans[i] = ans[i - 1] + 2;
}
}
cout<<ans[n];
}
4.D-tb的平方问题_牛客小白月赛101 (nowcoder.com)
观察到n很小,支持双重循环,那么我们就通过前缀和枚举所有区间的区间和,若该区间和是完全平方数那么就在ans数组中[i , j]差分加1即可,最后o1输出即可
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e5 + 10,inf = 0x3f3f3f3f,mod = 998244353;
int n,q;
ll a[N];
ll s[N],ans[N];
void insert(int x,int y)
{
ans[x]++;
ans[y + 1]--;
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>q;
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
s[i] = s[i - 1] + a[i];
}
for(int i = 1 ; i <= n ; i++)
{
for(int j = i ; j <= n ; j++)
{
ll sum = s[j] - s[i - 1];
ll t = sqrt(sum);
if(t * t == sum)
{
insert(i , j);
}
}
}
for(int i = 1 ; i <= n ; i++)
{
ans[i] += ans[i - 1];
}
while(q--)
{
int x;
cin>>x;
cout<<ans[x]<<"\n";
}
}
晚上又打了一场cf,做了三题,前两题十分水就不记录了,第三题是一道交互题,还是蛮有意思的,但是一开始想复杂了,只要一步步走思路还是比较好想的,猜一个二进制的数字,告诉你一共有n位,要求在2n次以内猜出来,那么我们只需要一位一位的猜即可,首先是每次向前拓展还是向后拓展,我们默认是向前拓展,当出现向前加0加1都不对的时候就要向后拓展了,我们默认每次拓展一个0,如果不对就把0改成1若对则继续下一位数字的循环若不对则说明我们已经猜出所有前缀,接下来往后猜,一样的思路,只不过我们此时不需要在加0错误后验证加1是否正确,若加1后s的长度小于n那么我们可以在提问验证之前再加一次0,若长度为n了那么退出即可。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e5 + 10,inf = 0x3f3f3f3f,mod = 998244353;
void work()
{
int n;
cin>>n;
string s;
s = "0";
int cnt = 0,l = 1,r = 0;
while(1)
{
cout<<"? "<<s<<"\n";
cout<<flush;
int f;
cin>>f;
if(f && s.size() == n)
{
break;
}
if(l)
{
if(f)
{
s.insert(0 , "0");
cnt = 0;
}else if(cnt == 0)
{
s.erase(0 , 1);
s.insert(0 , "1");
cnt++;
}else
{
s.erase(0 , 1);
s += "0";
cnt = 0;
l = 0,r = 1;
}
}else
{
if(f)
{
s += "0";
cnt = 0;
}else if(cnt == 0)
{
s.erase(s.size() - 1 , 1);
s += "1";
if(s.size() == n)
{
break;
}else
{
s += "0";
}
}
}
}
cout<<"! "<<s<<"\n";
cout<<flush;
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
work();
}
}