A
显然最后保留的是一段连续的长为
的数组,直接前缀和预处理+遍历长度为
的数组即可
void solve()
{
int n,k; cin>>n>>k; k = n-k;
vector<ll> pre(n+1);
ll res = 0;
for(int i=1; i<=n; ++i) {
cin>>pre[i];
pre[i] += pre[i-1];
if(i>=k) res = std::max(res, pre[i]-pre[i-k]);
}
cout<<res<<endl;
}
B
由于
和
不会重复,显然每次能删就删。对于删除,我们可以使用栈来维护
void solve()
{
int n; cin>>n;
std::string S; cin>>S;
std::stack<char> st;
for(auto &ch:S) {
if(!st.empty()) {
if(st.top()=='f' && ch=='c'){ st.pop(); continue; }
if(st.top()=='t' && ch=='b'){ st.pop(); continue; }
}
st.emplace(ch);
}
cout<<st.size()<<endl;
}
C
手玩一会,不难发现:我们无法通过传送门到达
和
。
考虑 gcd 性质,
,因此想到找到最接近
的一个位置,即
。因此最近的走法就是从
走到
,然后跳到
。再加上几个
比较小的时候的特判即可
void solve()
{
int n; cin>>n;
if(n==1){ cout<<0<<endl; return; }
if(n==2){ cout<<2<<endl; return; }
if(n==3){ cout<<4<<endl; return; }
if(n&1) cout<<6<<endl;
else cout<<4<<endl;
}
D
感觉比 C 题简单
假设
是满足题意的区间,那么这段区间对答案的贡献就是
上的所有答案
由于
比较小,直接
遍历所有区间,考虑贡献即可
计算的实现多种多样,包括但不限于差分树状数组、线段树、差分。我的代码中用的是差分树状数组
template <class T>
class FenwickTree {
private:
int n;
std::vector<T> a;
constexpr int lowbit(int x){ return x&-x; }
public:
FenwickTree(size_t _n): n(_n), a(_n+1) {}
void add(int pos, T v){ for(int i=pos; i<=n; i+=lowbit(i)) a[i] = a[i]+v; }
void init(size_t n, T val=T{}) {
this->n = n;
a.assign(n+1, val);
}
T sum(int x) {
T ans{};
for(int i=x; i>0; i-=lowbit(i)) ans = ans+a[i];
return ans;
}
T rangeSum(int l, int r){ return l<=r? sum(r)-sum(l-1):0; }
};
constexpr bool check(const int x) {
int t = sqrt(x);
return t*t==x;
}
void solve()
{
int n,q; cin>>n>>q;
vector<int> pre(n+1);
for(int i=1; i<=n; ++i) {
cin>>pre[i];
pre[i] += pre[i-1];
}
FenwickTree<ll> FT(n);
for(int L=1; L<=n; ++L) for(int R=L; R<=n; ++R) {
if(check(pre[R]-pre[L-1])) {
FT.add(L, 1);
if(R<n) FT.add(R+1, -1);
}
}
while(q--) {
int x; cin>>x;
cout<<FT.sum(x)<<endl;
}
}
E
对于一个数字
若是好的,那么他的所有因数都要出现在
中。手玩得出一下结论:
- 若
为好,那么
- 若
为坏,那么
由此我们可以使用类似于埃氏筛的做法,得到所有的好数字,埃氏筛时间复杂度为
,所有此代码复杂度
void solve()
{
int n; cin>>n;
std::set<int> st;
while(n--) {
int tt; cin>>tt;
st.emplace(tt);
}
int m = *st.rbegin();
vector<bool> good(m+1, true);
int res = 0;
for(int i=1; i<=m; ++i) {
if(good[i]==false) continue;
if(auto it=st.find(i); it==st.end()) {
for(int j=i; j<=m; j+=i)
good[j] = false;
}
if(good[i]) ++res;
}
cout<<res<<endl;
}
F
题目有点诈骗。
手玩发现:对于任何一个
,当且仅当
时无解,其他情况都可以有解
如果
在
中没有出现过,那么同理
可以出现在
的任何地方,于是我们使用类似于前缀和的方式,
cnt[i]
记录有多少个数字可以放在位置i
上。又由于假设前面已经放了suf
个数字,那么当前位置就有suf
个数字已经不能放了,所以答案于cnt[i]-suf
相乘
// 这里的 Z 使用的是 jiangly 的自动取模模板,太长了就不放出来了
void solve()
{
int n,w; cin>>n>>w;
vector<int> aa(n+1), bb(n+1);
for(int i=1; i<=n; ++i) cin>>aa[i];
for(int i=1; i<=n; ++i) cin>>bb[i];
vector<int> pos(n+1);
for(int i=1; i<=n; ++i) if(~aa[i]) pos[aa[i]] = i;
vector<int> cnt(n+1);
for(int i=n; i; --i) {
int R = std::min(n, i+w);
if(pos[bb[i]] && pos[bb[i]]>R) {
cout<<0<<endl; return;
}
if(!pos[bb[i]]) ++cnt[R];
}
Z res = 1;
int suf = 0;
for(int i=n; i; --i) {
cnt[i-1] += cnt[i];
if(~aa[i]) continue;
res *= cnt[i]-suf;
++suf;
}
cout<<res<<endl;
}