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\) 的段。