MX J-10 做题记录
A
https://cspjs.online/contest/264/problem/1
我们猜测出一定这个数一定要为 \(2^t\) 答案才最优。
因为 \(2l\le r\),所以 \([l,r]\) 区间中一定有一个 \(2\) 的整数次幂,枚举即可。
#include<bits/stdc++.h>
using namespace std;
inline void solve(){
int l,r,x;
cin>>l>>r;
x=1;int res=0;
while(x*2<=r){
res++;
x*=2;
}
cout<<res<<endl;
}
signed main(){
solve();
return 0;
}
赛时没有注意到 \(2l\le r\)。
B
https://cspjs.online/contest/264/problem/2
直接按位考虑。
对于第 \(k\) 位而言,这一位是 \(1\) 的条件是 \([l,r]\) 中有一个第 \(k\) 位为 \(1\) 的数。
这个玩意的条件是 \(r-l\ge 2^k\) 或 \(r_k=1\) 或 \(l_k=1\),其中 \(x_k\) 指 \(x\) 的第 \(k\) 位。
#include<bits/stdc++.h>
#define ans sum
using namespace std;
int q,mier[31];
int main(){
freopen("or.in","r",stdin);
freopen("or.out","w",stdout);
scanf("%d",&q);
mier[0]=1;
for(int i=1;i<=30;i++)mier[i]=mier[i-1]*2;
for(int i=1,l,r;i<=q;i++){
scanf("%d%d",&l,&r);
int sum=0;
for(int i=30;i>=0;i--){
if(r-l>=mier[i]){
sum+=mier[i];
continue;
}
int x=(l>>i)&1,y=(r>>i)&1;
if(x==0&&y==0);else ans+=mier[i];
}
printf("%d\n",ans);
}
}
赛时过了。
C
https://cspjs.online/contest/264/problem/3
设 \(ed_x\) 表示 \(x\) 出现的最后一个位置,那么 \(1 \to ed_x\) 这个前缀都能取到最大值,这是显然的。那么就好做了,用 \(set\) 维护 \(ed_x\) 组成的集合,每次取出最小的一个,每次会询问一个区间的最小或最大值,因为左右端点右移,可以利用单调队列优化。
#include <bits/stdc++.h>
using namespace std;
int main() {
freopen("subsequence.in", "r", stdin);
freopen("subsequence.out", "w", stdout);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int tt = 1;
while (tt--) {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
a[i]--;
}
vector<int> l(n + 1, INT_MAX);
for (int i = 0; i < n; i++) l[a[i]] = i;
priority_queue<int, vector<int>, greater<int>> q(l.begin(), l.end());
priority_queue<array<int, 2>, vector<array<int, 2>>, greater<array<int, 2>>> q1, q2;
vector<bool> vis(n + 1, false);
for (int i = 0; i <= q.top(); i++) {
q1.push({ -a[i], i });
q2.push({ a[i], i });
}
vector<int> ans;
int i = 0;
while (!q2.empty()) {
auto t = (ans.size() % 2 == 0 ? q1.top() : q2.top());
int x = t[0], pos = t[1];
if (ans.size() % 2 == 0) {
q1.pop();
x *= -1;
} else {
q2.pop();
}
ans.emplace_back(x);
i = pos + 1, vis[x] = true;
while ((q.top() != INT_MAX) && vis[a[q.top()]]) {
int j = q.top();
q.pop();
for (int k = j + 1; k <= min(q.top(), n - 1); k++) {
q1.push({ -a[k], k });
q2.push({ a[k], k });
}
}
while (!q1.empty() && (vis[-q1.top()[0]] || q1.top()[1] < i)) q1.pop();
while (!q2.empty() && (vis[q2.top()[0]] || q2.top()[1] < i)) q2.pop();
}
cout << ans.size() << '\n';
for (auto x : ans) cout << x + 1 << ' ';
cout << '\n';
}
return 0;
}
赛时这个连看都没看
D
https://cspjs.online/contest/264/problem/4
容易知道,每段的元素是什么并不重要,只要知道划分方式就能推出唯一的 \(b\)。比较特殊的情况是除了开头的段与结尾的段,如果有长度为 \(2\) 的段,其等价于两个长度为 \(1\) 的段,对应的 都是 1 1 ,于是直接钦定除了开头和结尾不能出现长度为 \(2\) 的段。考虑直接 dp,\(dp_{i,j}\) 表示到了 \(i\) 划分出了 \(j\) 段,这样是 。
注意到划分出 \(k\to n\) 段的方案是等价的,因为只要求 \(1\to k\) 在 \(a\) 中都至少出现了一次。所以只要从 \(1-k\) dp 即可。
因为转移是标准的求一个区间的和,前缀和优化即可。
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7,K=15,p=998244353;
int n,k,f[N][K],g[N][K];
int add(int a,int b){
if(a+b>=p) return a+b-p; return a+b;
}
int dec(int a,int b){
if(a-b<0) return a-b+p; return a-b;
}
inline void solve(){
cin>>n>>k;
f[0][0]=g[0][0]=1;
for(int i=1;i<=n;i++){
g[i][0]=1;
for(int j=1;j<=k;j++){
f[i][j]=g[i-1][j-1];
if(i>2&&i!=n)f[i][j]=dec(f[i][j],f[i-2][j-1]);
g[i][j]=add(g[i-1][j],f[i][j]);
}
f[i][k+1]=add(g[i-1][k],g[i-1][k+1]);
if(i>2&&i!=n)f[i][k+1]=dec(f[i][k+1],add(f[i-2][k],f[i-2][k+1]));
g[i][k+1]=add(g[i-1][k+1],f[i][k+1]);
}
cout<<add(f[n][k],f[n][k+1])<<endl;
}
signed main(){
solve();
return 0;
}
赛时没有注意到除了开头和结尾不能出现长度为 \(2\) 的段。

浙公网安备 33010602011771号