比赛链接:
https://codeforces.com/contest/1720
D. Xor-Subsequence
题意:
长为 的序列,找到最长的子序列 ,设序列 长为 ,使得 ,序列下标都从 0 开始。
思路:
转化一下关系 。
考虑简单的 。在满足上述条件下,。会超时。
因为 所以意味着 的变化不会超过 8 位,即 。
所以优化一下, 不用从 0 开始,从 开始就行。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
int n;
cin >> n;
vector <LL> a(n), dp(n, 1);
for (int i = 0; i < n; i ++ ){
cin >> a[i];
for (int j = max(0, i - 256); j < i; j ++ ){
if ((a[j] ^ i) < (a[i] ^ j)){
dp[i] = max(dp[i], dp[j] + 1);
}
}
}
cout << *max_element(dp.begin(), dp.end()) << "\n";
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
当 的范围扩大到 1e9 的时候,上述方法就不行了。
,意味着在二进制的状态下,前 位是相同的,即对于前 位来说,,两边都乘上 ,得到 。
对于第 位,,即 ,。
得到 ,那么 。
考虑将 插入字典树中,插入的时候,因为 ,所以每一位将 都要记录下来,更新对应位置的最大值。
查询的时候,要导入 ,找到 的最大值。
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 3e5 + 10;
struct Trie{
LL ch[N * 30][2], f[N * 30][2], idx = 0;
void reset(){
for (int i = 0; i <= idx; i ++ ){
for (int j = 0; j < 2; j ++ ){
ch[i][j] = f[i][j] = 0;
}
}
idx = 0;
}
void insert(LL x, LL t, LL mx){
LL u = 0;
for (int i = 30; ~ i; i -- ){
LL &v = ch[u][x >> i & 1];
if (!v) v = ++ idx;
u = v;
f[u][t >> i & 1] = max(f[u][t >> i & 1], mx);
}
}
LL query(LL x, LL id){
LL u = 0, res = 1;
for (int i = 30; ~ i; i -- ){
LL v = x >> i & 1;
res = max(res, f[ch[u][!v]][!(id >> i & 1)] + 1);
if (!ch[u][v]) break;
u = ch[u][v];
}
return res;
}
}trie;
void solve(){
int n;
cin >> n;
vector <LL> a(n);
for (int i = 0; i < n; i ++ )
cin >> a[i];
vector <LL> dp(n);
for (int i = 0; i < n; i ++ ){
dp[i] = trie.query(a[i] ^ i, a[i]);
trie.insert(a[i] ^ i, i, dp[i]);
}
cout << *max_element(dp.begin(), dp.end()) << "\n";
trie.reset();
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int T;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
E. Misha and Paintings
题意:
的矩阵,每个位置有一个数字 ,每次可以选择一个正方形将其中的元素全部改成 ,问最少多少次操作可以使得矩阵中的不同数字的值等于 。
思路:
统计矩阵中不同数字的个数 ,如果 ,那么最少 次。
否则答案可以通过构造的操作,至多为二。
在矩阵中选择一个矩阵,将其改为某个元素,此操作可以让整个矩阵中不同元素的数量 ,如果为 或者 ,可以根据填充的不同数字使得得到答案 。
如果不是,记该矩阵右下角坐标为 ,选择 作为第二次操作的矩阵的右下角,往左上扩张,此操作可以让整体矩阵不同元素的数量减少 0 或 1 或 2,由此至多为 2。
矩阵覆盖何时可以让某个值消失,需要记录该元素的横纵坐标的最大最小值,通过二维前缀和/差分去进行快速统计。
代码:
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, k;
cin >> n >> k;
vector<vector<int>> a(n + 1, vector<int>(n + 1));
vector<bool> has(n * n + 1);
int cnt = 0;
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= n; j ++ ){
cin >> a[i][j];
if (!has[a[i][j]]){
cnt ++ ;
has[a[i][j]] = true;
}
}
}
if (cnt <= k){
cout << k - cnt << "\n";
return 0;
}
vector<int> xmin(n * n + 1, n + 1), ymin(n * n + 1, n + 1), xmax(n * n + 1, 0), ymax(n * n + 1, 0);
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= n; j ++ ){
xmin[a[i][j]] = min(xmin[a[i][j]], i);
ymin[a[i][j]] = min(ymin[a[i][j]], j);
xmax[a[i][j]] = max(xmax[a[i][j]], i);
ymax[a[i][j]] = max(ymax[a[i][j]], j);
}
}
for (int len = 1; len < n; len ++ ){
vector<vector<int>> sum(n + 2, vector<int>(n + 2));
for (int i = 1; i <= n * n; i ++ ){
if (!has[i] || xmax[i] - xmin[i] + 1 > len || ymax[i] - ymin[i] + 1 > len) continue;
int x = max(1, xmax[i] - len + 1);
int y = max(1, ymax[i] - len + 1);
sum[x][y] ++ ;
sum[x][ymin[i] + 1] -- ;
sum[xmin[i] + 1][y] -- ;
sum[xmin[i] + 1][ymin[i] + 1] ++ ;
}
for (int i = 1; i <= n; i ++ ){
for (int j = 1; j <= n; j ++ ){
sum[i][j] += sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
if (cnt - sum[i][j] == k || cnt - sum[i][j] == k - 1){
cout << "1\n";
return 0;
}
}
}
}
cout << "2\n";
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现