ABC 408
A:水
B:水
C:差分数组模板题
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include <sstream>
#include <vector>
using namespace std;
const double eps = 1e-10;
const int arrsize = 1e6+6;
int num[200002];
int lnum[200002],rnum[200002];
int pre[arrsize],ans;
int H,W,N,M,xb,S,tp,fg,lid,rid;
int main() {
scanf("%d %d",&N,&M);
xb = 0;fg = 0;
ans = arrsize;
for(int i=1;i<=M;i++){
scanf("%d %d",&lid,&rid);
pre[lid]++;
pre[rid+1]--;
}
for(int i=1;i<=N;i++){
xb += pre[i];
if(xb < ans){
ans = xb;
}
}
printf("%d",ans);
return 0;
}
/*
5
5 8 8 8 1
*/
D:不难,不过不用笔化公式就不太好想
首先可以确定的是,化简后的序列形式为0n+1m+0*k,且n,m,k为任意数
设序列中1组成的子序列从l开始r结束,并pre1[i]为前i位里1的个数
则需要改变的次数为(序列长为n)
\[pre1[l-1]+[(r-l+1)-(pre1[r]-pre1[l-1])]+pre1[n]-pre1[r]
\]
化简后
\[pre1[n] + 2pre1[l-1]-(l-1)-(2pre1[r]-r)
\]
枚举l,r形成双循环?那时间复杂度就超了
事实上,当我们枚举r时已经可以确定$$pre1[n] -(2pre1[r]-r)$$
同时可以提前处理得到r前最小的$$2pre1[l-1]-(l-1)$$
做完了
#include<iostream>
#include<cstdio>
using namespace std;
const int arrsize = 1e6+6;
int pre1[arrsize];
char s[arrsize];
int test, n;
int main() {
scanf("%d", &test);
while (test--) {
scanf("%d %s", &n, s+1); // 从s[1]开始存储
// 计算pre1数组
pre1[0] = 0;
for (int i = 1; i <= n; i++) {
pre1[i] = pre1[i-1] + (s[i] == '1');
}
int min_l = 0; // 维护2*pre1[l-1]-(l-1)的最小值
int ans = pre1[n]; // 初始化为全0的情况
for (int r = 1; r <= n; r++) {
// 更新min_l
if (2 * pre1[r-1] - (r-1) < min_l) {
min_l = 2 * pre1[r-1] - (r-1);
}
// 计算当前r对应的最优解并更新答案
ans = min(ans, pre1[n] + min_l - (2 * pre1[r] - r));
}
printf("%d\n", ans);
}
return 0;
}
E:最短路要求的权值和最小改为权值或最小。
容易发现的性质有两个(或的性质)
1:只要答案中的某个权值在第i位是1,那么答案里必然该位是1。
2:答案与答案包括的每个权值的&的结果都是该权值
先设置一个最大值x,令每一位都为1表示取所有边;从最大位起考虑每一位,先将该位置为0,并将其&上所有边权,如果结果仍为边权则假定该边在该答案内,并使用并查集将点连通;
最后判断点1和点N是否连通,如果不连通则说明该位必然有1。
完了。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 2e5 + 5;
int n, m, u, v, w, fa[maxn], x = (1 << 30) - 1;
struct Edge {
int u, v, w;
};
vector<Edge> edges;
int find(int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
int main() {
scanf("%d %d", &n, &m);
edges.resize(m + 1);
for (int i = 1; i <= m; i++) {
scanf("%d %d %d", &u, &v, &w);
edges[i] = {u, v, w};
}
for (int k = 29; k >= 0; k--) {
x ^= 1 << k;//这里写的很漂亮,换我就写(1<<k)-1了
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= m; i++) {
u = edges[i].u;
v = edges[i].v;
w = edges[i].w;
if ((w & x) == w) fa[find(v)] = find(u);
}
if (find(1) != find(n)) x ^= 1 << k;
}
printf("%d", x);
return 0;
}
F:第一次想出来F题,做了整一天,纪念一下。思路比较简单,首先抓住重要性质:H是一个1-N的序列,这样保证不会有相同的数字。
令ans[i]为i处可移动次数,基本思想就是,$$ans[i]= max(ans[j])+1,j∈[i-R,i+R],H[i]-H[j]>=D$$
考虑线段树维护ans以快捷完成单点修改和区间查询,将H数组中权值与位置打包存储并按权值从小到大排序,使用队列q暂缓存储D个最新值以保证当前ans[i]只能统计到合法的ans[j].
最后注意除了在线段树中找最大值还要在q中找!!!坑了我一下午
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
#include<algorithm>
#include <sstream>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 500005;
int N, D, R;
int H[MAXN];
struct Node {
int pos;
int height;
bool operator < (const Node &fk)const{
return height < fk.height;
}
}q[MAXN];
struct SegmentTree {
int val;
int ls;
int rs;
}st[MAXN<<2];
void pushup_max(int rt){
st[rt].val = max(st[rt<<1].val , st[rt<<1|1].val);
}
void build(int rt,int l,int r){
st[rt].ls = l;
st[rt].rs = r;
if(l == r){
st[rt].val = -1;
return ;
}
int mid = (l + r) >> 1;
build(rt<<1,l,mid);
build(rt<<1|1,mid + 1,r);
pushup_max(rt);
}
int query_max(int rt,int l,int r){
if(l <= st[rt].ls && st[rt].rs <= r){
return st[rt].val;
}
int mid = (st[rt].ls + st[rt].rs)>>1;
// cout<<rt<<" "<<st[rt].ls<<" "<<st[rt].rs<<" "<<l<<" "<<r<<" "<<mid<<endl;
if(l > mid){
// cout<<"向右"<<(rt<<1|1)<<endl;
return query_max(rt<<1|1,l,r);
}
if(r <= mid){
// cout<<"向左"<<(rt<<1)<<endl;
return query_max(rt<<1,l,r);
}
// cout<<"分段"<<endl;
int a = query_max(rt<<1,l,mid);
// cout<<"从a回"<<a<<endl;
int b = query_max(rt<<1|1,mid+1,r);
// cout<<"从b回"<<b<<endl;
return max(a,b);
}
void sgupdate_max(int rt,int val,int pos){
if(st[rt].ls == pos && st[rt].rs == pos){
st[rt].val = val;
return ;
}
int mid = (st[rt].ls + st[rt].rs)>>1;
if(pos <= mid){
sgupdate_max(rt<<1,val,pos);
}else{
sgupdate_max(rt<<1|1,val,pos);
}
pushup_max(rt);
return;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("mysol.txt", "w", stdout);
scanf("%d %d %d", &N, &D, &R);
for (int i = 1; i <= N; i++) {
scanf("%d", &H[i]);
}
build(1,1,N);
for (int i = 0; i < N; i++) {
q[i] = {i + 1, H[i + 1]};
}
sort(q, q+N);
queue<pair<int, int>> myq;
for (int i = 0;i < N; i++){
if((int)myq.size() == D){
sgupdate_max(1,myq.front().second,myq.front().first);
myq.pop();
}
int xb = q[i].pos;
int l = max(1,xb - R);
int r = min(N,xb + R);
int tp = query_max(1,l,r);
myq.push(make_pair(xb,tp+1));
}
int ass = -1;
while(!myq.empty()){
ass = max(ass,myq.front().second);
myq.pop();
}
printf("%d", max(ass,query_max(1,1,N)));
return 0;
}