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

posted @ 2024-12-15 22:42  Weslie_qwq  阅读(21)  评论(0)    收藏  举报