标签:include bool puts void 主席树 val 长度 解题报告 upd
目标区间右端点一定是所有给定区间左端点的最大值。
目标区间左端点一定是所有给定区间右端点的最小值。
输出即可,注意负数。
#include<bits/stdc++.h>
using namespace std;
int T, n, a, b;
int main()
{
cin >> T;
while(T--)
{
int a = 0, b = 1e9;
scanf("%d", &n);
for(int i = 1, x, y; i <= n; i++)
{
scanf("%d%d", &x, &y);
a = max(a, x); b = min(b, y);
}
cout << max(0, a - b) << endl;
}
return 0;
}
贪心。
假设输入序列为\(a\)序列,答案序列为\(b\)序列。
那么如果\(a\)序列上升了,\(b\)序列自然就是\(a\)序列的值。
如果\(a\)序列不变,那么就从没有出现的数字中取最小的数字。
贪心过程中注意特判无解情况。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
int T, n;
int a[maxn];
int b[maxn];
void solve()
{
scanf("%d", &n); set<int> s;
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i]);
s.insert(i);
}
int mx = 0;
for(int i = 1; i <= n; i++)
{
if(a[i] > mx)
{
if(s.count(a[i]))
{
b[i] = a[i];
s.erase(b[i]);
mx = a[i];
} else{
puts("-1");
return;
}
}
else if(a[i] == mx)
{
if(*s.begin() > mx)
{
puts("-1");
return;
}
b[i] = *s.begin();
s.erase(s.begin());
}
}
for(int i = 1; i <= n; i++)
printf("%d ", b[i]); puts("");
}
int main()
{
scanf("%d", &T);
while(T--) solve();
return 0;
}
首先这题初看很难做,但其实分析一下就可以转换为一个构造题。
首先长度为\(n\)的序列经过\(n\)次的操作一定能变成任意的括号序列。
那么其实我们只需要把序列构造成满足有\(k\)个合法前缀的情况。
怎么构造呢,先加入\(k-1\)个\(()\),然后剩余的全部都加上\(((..))\)这样的。
打个比方:
之后根据最终的序列构造操作。
#include<bits/stdc++.h>
#define PII pair<int, int>
using namespace std;
const int maxn = 2000 + 10;
int T, n, k;
string s, ans;
void swap_str(int x, int y)
{
if(x > y) swap(x, y);
while(x < y)
{
swap(s[x], s[y]);
x++, y--;
}
}
void get_op()
{
vector<PII> op;
for(int i = 0; i < (int)s.size(); i++)
{
if(s[i] != ans[i])
{
for(int j = i+1; j < (int)s.size(); j++)
{
if(s[i] != s[j])
{
swap_str(i, j);
op.push_back({i+1, j+1});
break;
}
}
}
}
cout << op.size() << endl;
for(auto x : op)
printf("%d %d\n", x.first, x.second);
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &k);
cin >> s; ans = "";
for(int i = 1; i <= k - 1; i++)
ans += '(', ans += ')';
int tmp = n - (k-1) * 2;
for(int i = 1; i <= tmp / 2; i++) ans += '(';
for(int i = 1; i <= tmp / 2; i++) ans += ')';
//cout << ans << endl;
get_op();
}
return 0;
}
主席树+贪心。
我们需要满足子序列和最大,所以这\(k\)个数,一定是我们将序列从大到小排序后的前\(k\)个数字。
于是乎我要选择的\(k\)个数确定了。
同时我们需要保证字典序最小。
设\(num\)为第\(k\)大的元素,要想让字典序最小,那么我们需要做的就是让所有\(num\)出现的位置尽可能靠前,也就是尽可能的挑选\(idx\)小的元素。
所以我们记录序列\(val\)时同时记录序列的\(id\),之后按照\(val\)排序,\(val\)相等的两项让\(id\)小的在前。
之后用主席树维护\(id\)。
对于每次询问,找到对应的树与空树进行查询第\(pos\)大的数在原序列的位置,找到这个数即为答案。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n, m, mp[maxn]; //mp(i)表示第i次询问对应的那个版本的线段树
struct Arr
{
int v, id;
}a[maxn];
int b[maxn];
bool cmp(Arr a, Arr b)
{
if(a.v == b.v) return a.id < b.id;
return a.v > b.v;
}
struct Node{
int k, pos, id;
}q[maxn];
bool cmp1(Node a, Node b){
return a.k < b.k;
}
int ans[maxn];
//-----------主席树部分
int sum[maxn<<5], ls[maxn<<5], rs[maxn<<5];
int rt[maxn<<5], tot;
int build(int l, int r)
{
int root = ++tot;
if(l == r) return root;
int mid = (l + r) >> 1;
ls[root] = build(l, mid);
rs[root] = build(mid+1, r);
return root;
}
int update(int pre, int l, int r, int k)
{
int root = ++tot;
ls[root] = ls[pre], rs[root] = rs[pre], sum[root] = sum[pre] + 1;
if(l == r) return root;
int mid = (l + r) >> 1;
if(k <= mid) ls[root] = update(ls[pre], l, mid, k);
else rs[root] = update(rs[pre], mid+1, r, k);
return root;
}
int query(int u, int v, int l, int r, int k)
{
if(l == r) return l;
int x = sum[ls[v]] - sum[ls[u]];
int mid = (l + r) >> 1;
if(k <= x) return query(ls[u], ls[v], l, mid, k);
else return query(rs[u], rs[v], mid+1, r, k - x);
}
//-------------------
void init()
{
scanf("%d", &n);
for(int i = 1; i <= n; i++)
{
scanf("%d", &a[i].v);
a[i].id = i;
b[i] = a[i].v;
} sort(a+1, a+1+n, cmp);
scanf("%d", &m);
for(int i = 1, x, y; i <= m; i++){
scanf("%d%d", &x, &y);
q[i] = {x, y, i};
} sort(q+1, q+1+m, cmp1);
}
int main()
{
init();
rt[0] = build(1, n);
int p = 1;
for(int i = 1; i <= m; i++)
{
if(q[i].k < p)
{
mp[i] = mp[i-1]; //k相等的话用的是同一棵线段树
continue;
}
//插入对应的id
for(int j = p; j <= q[i].k; j++)
rt[j] = update(rt[j-1], 1, n, a[j].id);
mp[i] = rt[q[i].k];
p = q[i].k + 1;
}
for(int i = 1; i <= m; i++)
{
int t = query(rt[0], mp[i], 1, n, q[i].pos);
ans[q[i].id] = b[t];
}
for(int i = 1; i <= m; i++)
printf("%d\n", ans[i]);
return 0;
}
\(dp\)。
设\(f(i,j)\)表示考虑到了第\(i\)题,交换后比交换前多得\(j\)分的结果数。
如果\(h(i)==h(i+1)\)的话,\(f(i,j)=f(i-1,j)\),因为这时候交换并不会增加结果。
其他情况,\(f(i,j)=f(i-1,j+1)+f(i-1,j-1)+(k-2)*f(i-1,j)\)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
const int mod = 998244353;
ll n, k, h[maxn], f[maxn][maxn<<1];
int main()
{
ios::sync_with_stdio(false); cin.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++)
cin >> h[i];
if(k == 1)
{
puts("0");
return 0;
}
f[0][2001] = 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j < maxn<<1; j++)
{
if(h[i] == h[i%n+1]) f[i][j] = (f[i-1][j]*k)%mod;
else f[i][j] = (f[i-1][j-1]+f[i-1][j+1]+f[i-1][j]*(k-2))%mod;
} ll ans = 0;
for(int i = 1; i <= n; i++)
ans = (ans + f[n][2001+i]) % mod;
cout << ans << endl;
return 0;
}
Codeforces Round #602 (Div. 2, based on Technocup 2020 Elimination Round 3) 解题报告(A~F)(E/F(hard)无)
标签:include bool puts void 主席树 val 长度 解题报告 upd
原文地址:https://www.cnblogs.com/zxytxdy/p/12246009.html