@2368860385
2020-11-07T03:05:15.000000Z
字数 9985
阅读 182
正睿提高
2018.9.15
放假回家,然后没做。。。
回来看了题之后,发现一个都不会做。。。
每个点只能买或者卖。
第一档:3^n枚举每个点是买/卖/不操作
第二档:状压前面没有买的点。
第三档:dp,f[i][j]表示到第i个位置,买了j个的获益。然后判断i是买/卖/不操作。个人理解:第二维在转移时好像并没有用,因为这j个也不知道是哪些,获得的收益也不知道从哪里买的。但是去掉这一维发现不能转移了(转移时不会买,因为一旦买就成了负数了),转移时i只有三种情况买/卖/不操作,为了可以转移,必须要满足三种情况可以转移(从哪转移,贡献怎么算),发现转移会影响当前买的个数,于是加上这个限制条件就可以转移了。
第四档:直接判断第i个位置是否可以卖出。然后从前面所有没有买的找最小的,从所有卖出的位置替换(表示在i这个位置卖),如果都不能获得收益,则只能在i这里买了。注意:为了让操作次数更少,于是先考虑在i卖出,可能代替某些位置。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
const int N = 50010;
int a[N];
int n;
priority_queue< int, vector<int>, greater<int> > q1, q2;
void init() {
n = read();
for (int i=1; i<=n; ++i) a[i] = read();
while (!q1.empty()) q1.pop();
while (!q2.empty()) q2.pop();
}
void solve() {
LL Ans = 0;
for (int i=1; i<=n; ++i) {
int opt = 0, mx = 0;
if (!q2.empty()) { // 卖出的,优先考虑,减少购买次数
if (mx < a[i] - q2.top())
mx = a[i] - q2.top(), opt = 1;
}
if (!q1.empty()) { // 未买入的
if (mx < a[i] - q1.top())
mx = a[i] - q1.top(), opt = 2;
}
Ans += mx;
if (opt == 0) q1.push(a[i]);
else if (opt == 1) q1.push(q2.top()), q2.pop(), q2.push(a[i]);
else q1.pop(), q2.push(a[i]);
}
cout << Ans << " " << (int)(q2.size()) * 2 << "\n";
}
int main() {
int T = read();
while (T--) {
init();
solve();
}
return 0;
}
二分一个直径d,判断能否穿过。
判断是否穿过有点难,正难则反,判断是否不能穿过。
假设L上方有一个点为S,x轴下方有一个点为T,如果两点之间的距离小于d,那么连一条边,如果S与T联通了,那么说明直径为d的球无法穿过。复杂度
也就是求最小瓶颈生成树。
瓶颈生成树 :无向图G的一颗瓶颈生成树是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。瓶颈生成树的值为T中最大权值边的权。
无向图的最小生成树一定是瓶颈生成树。瓶颈生成树不一定是最小生成树。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;
const double eps = 1e-8;
const int N = 505;
int fa[N];
int n, S, T;
double x[N], y[N], L;
int find(int x) {
return x == fa[x] ? x : fa[x] = find(fa[x]);
}
void Merge(int x,int y) {
x = find(x), y = find(y);
fa[x] = y;
}
double Calc(int i,int j) {
return sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]));
}
bool check(double d) {
for (int i=1; i<=n+2; ++i) fa[i] = i;
for (int i=1; i<=n; ++i) {
if (d > y[i]) Merge(T, i); // 居然写成了x[i]
if (d > L - y[i]) Merge(S, i);
for (int j=i+1; j<=n; ++j)
if (d > Calc(i, j)) Merge(i, j);
}
if (find(S) == find(T)) return false;
return true;
}
int main() {
scanf("%d%lf",&n,&L);
S = n + 1, T = n + 2;
for (int i=1; i<=n; ++i)
scanf("%lf%lf",&x[i],&y[i]);
double l = 0, r = L, ans = 0;
while (r - l > eps) {
double mid = (l + r) / 2.0;
if (check(mid)) ans = mid, l = mid;
else r = mid;
}
printf("%.3lf",ans);
return 0;
}
题意:一个长度为N的01序列。
现在有Q次询问,每次询问一个区间[L,R],问在这个区间中最少需要删掉多少个数,使得这个区间剩下的每个非空前缀以及每个非空后缀中,0的个数都不小于1的个数。
(老是想成将1变成0。。。)
两种做法。
首先都是将字符串中的0变成1,1变成-1。不合法=>区间和为负
1、线段树求最小的“前缀+后缀”
结论:答案就是在查询区间内,选一个前缀,一个后缀(要求不重复),然后加起来,取最小值。
就是
证明?(测试一下小于1000的数据?)
于是就可以线段树维护了。
首先让每个节点的值直接是前缀和,和后缀和,最后答案=ans-pre[l-1]-suf[r+1]就行了。
每个节点维护三个值:vl,vr,val,表示在此区间内的前缀最大值,后缀最大值,前缀和后缀都在此区间的答案的最大值(l,r都在此区间的,才更新)。
查询的时候是这样的:
_ | _ _ | _ _ _ _
l,r都在左边的线段树维护了,都在右边的也维护了。还有一个在左边一个在右边,这样没有更新到。由于每次更新一段区间一定是从左往右更新的,那么记录一个值表示当前区间的左边的所有区间内的前缀最大值(比如上图中到第三个区间的时候,更新出了前两个区间的前缀最大值,即123),然后可以直接和这个区间的后缀最大值合并了。
2、倍增
每次询问:从左忘右扫一遍,删除必须要删除的1,(为了是所有非空前缀满足条件),然后为了让所有非空后缀满足条件,然后在删掉1后的序列上,求一个最小后缀和,ans=删掉的1的个数+(-最小后缀和)
题解:
考虑用倍增加速删除的过程。
每个点记录右边第一个删除的位置,显然是一个树结构。
然后删除这个点后,剩下很多段,然后合并这些段求出最小后缀值。
011100111
0走到第3个位置,剩下的段是01,然后在走到第4个位置,没有剩下的,然后再走到最后一个1的位置,剩下的是0011,合起来就是010011,求出最小后缀和。
两段合并的过程是可以倍增加速的。就是一次走2^j步。最后一个段不一定走完,特判。
线段树维护前缀最小 + 后缀最小
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
const int N = 200005;
const int INF = 1e9;
char s[N];
int a[N], val[N << 2], vl[N << 2], vr[N << 2], pre[N], suf[N];
int Ans, VL;
void solve1(int n, int Q) {
while (Q--) {
int l = read(), r = read(), ans = INF, minL = INF;
for (int sum=0,i=l-1; i<=r; ++i) {
minL = min(minL, pre[i] - pre[l - 1]);
ans = min(ans, minL + (suf[i + 1] - suf[r + 1]));
}
printf("%d\n",-ans);
}
}
#define Root 0, n + 1, 1
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
void pushup(int rt) {
vl[rt] = min(vl[rt << 1], vl[rt << 1 | 1]);
vr[rt] = min(vr[rt << 1], vr[rt << 1 | 1]);
val[rt] = min(vl[rt << 1] + vr[rt << 1 | 1], min(val[rt << 1], val[rt << 1 | 1]));
}
void build(int l,int r,int rt) {
if (l == r) {
vl[rt] = pre[l], vr[rt] = suf[l], val[rt] = INF;
return ;
}
int mid = (l + r) >> 1;
build(lson);build(rson);
pushup(rt);
}
void query(int l,int r,int rt,int L,int R) {
if (L <= l && r <= R) {
if (VL == INF) Ans = val[rt], VL = vl[rt];
else {
Ans = min(Ans, val[rt]);
Ans = min(Ans, VL + vr[rt]);
VL = min(VL, vl[rt]);
}
return ;
}
int mid = (l + r) >> 1;
if (L <= mid) query(lson, L, R);
if (R > mid) query(rson, L, R);
}
int main() {
int n = read(), Q = read();
scanf("%s",s + 1);
for (int i=1; i<=n; ++i)
a[i] = s[i] == '0' ? 1 : - 1;
for (int i=1; i<=n; ++i) pre[i] = pre[i - 1] + a[i];
for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i];
if (n <= 1000) { solve1(n, Q); return 0; }
build(Root);
while (Q --) {
int l = read(), r = read();
l --, r ++;
VL = Ans = INF;
query(Root, l, r);
Ans = Ans - pre[l] - suf[r];
printf("%d\n",-Ans);
}
return 0;
}
倍增。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
const int N = 200005;
const int INF = 1e9;
char s[N];
int a[N], pre[N], suf[N], sk[N], st[N][20];
struct Node{
int pos, sum, val, cnt; // 分别表示位置,和,最小后缀和,要删掉个数。
}f[N][20];
Node operator + (Node A, Node B) {
Node C;
C.pos = B.pos;
C.sum = A.sum + B.sum;
C.val = min(B.val, B.sum + A.val);
C.cnt = A.cnt + B.cnt;
return C;
}
int getsum(int l,int r) {
if (l > r) return 0;
return suf[l] - suf[r + 1];
}
int getmin(int l,int r) {
if (l > r) return INF;
int t = log2(r - l + 1);
return min(st[l][t], st[r - (1<<t) + 1][t]);
}
void work() {
int l = read(), r = read();
if (l == r) {
if (s[l] == '1') puts("1");
else puts("0");
return ;
}
Node ans;
ans.pos = l; ans.val = INF; ans.sum = 0; ans.cnt = 0;
for (int j=19; j>=0; --j)
if (f[ans.pos][j].pos <= r)
ans = ans + f[ans.pos][j];
if (ans.pos < r) {
int sum = getsum(ans.pos + 1, r);
int mn = getmin(ans.pos + 1, r) - suf[r + 1];
ans.val = min(0, min(ans.val + sum, mn));
}
if (a[l] == -1) ans.cnt ++;
if (ans.cnt == (r-l+1)) ans.val = 0;
printf("%d\n", ans.cnt + (-ans.val));
}
int main() {
int n = read(), Q = read();
scanf("%s",s + 1);
for (int i=1; i<=n; ++i)
a[i] = s[i] == '0' ? 1 : - 1;
memset(st, 0x3f, sizeof(st));
for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i], st[i][0] = suf[i];
for (int j=1; j<=19; ++j)
for (int i=1; (i+(1<<(j-1)))<=n; ++i) {
st[i][j] = min(st[i][j - 1], st[i + (1<<(j-1))][j - 1]);
}
int top = 1;sk[top] = n + 1;
for (int p,i=n; i>=1; --i) {
if (s[i] == '0') {
if (top > 1) top --;
}
p = sk[top];
f[i][0].pos = p, f[i][0].sum = getsum(i + 1, p - 1), f[i][0].val = getmin(i + 1, p - 1) - suf[p];
f[i][0].cnt = 1;
if (s[i] == '1') sk[++top] = i;
}
for (int j=0; j<=19; ++j) f[n+1][j].pos = n + 1;
for (int j=1; j<=19; ++j)
for (int i=1; i<=n; ++i)
f[i][j] = f[i][j - 1] + f[f[i][j-1].pos][j - 1];
while (Q--) work();
return 0;
}
调试代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
const int N = 200005;
const int INF = 1e9;
char s[N];
int a[N], pre[N], suf[N], sk[N], st[N][20];
struct Node{
int pos, sum, val, cnt;
}f[N][20];
Node operator + (Node A, Node B) {
Node C;
C.pos = B.pos;
C.sum = A.sum + B.sum;
C.val = min(B.val, B.sum + A.val);
C.cnt = A.cnt + B.cnt;
return C;
}
void solve1(int n, int Q) {
while (Q--) {
int l = read(), r = read(), ans = 0;
for (int sum=0,i=l; i<=r; ++i) {
sum += a[i];
if (sum < 0) sum = 0, ans ++, a[i] = 0;
}
for (int sum=0,i=r; i>=l; --i) {
sum += a[i];
if (sum < 0) sum = 0, ans ++, a[i] = 0;
}
for (int i=l; i<=r; ++i)
if (!a[i]) a[i] = -1;
printf("%d\n",ans);
}
}
int getsum(int l,int r) {
if (l > r) return 0;
return suf[l] - suf[r + 1];
}
int getmin(int l,int r) {
if (l > r) return INF;
int t = log2(r - l + 1);
return min(st[l][t], st[r - (1<<t) + 1][t]);
}
void work() {
int l = read(), r = read();
if (l == r) {
if (s[l] == '1') puts("1");
else puts("0");
return ;
}
Node ans;
ans.pos = l; ans.val = INF; ans.sum = 0; ans.cnt = 0;
for (int j=19; j>=0; --j) {
// cout << f[ans.pos][j].pos << "\n";
if (f[ans.pos][j].pos <= r) {
ans = ans + f[ans.pos][j];
// cout << ans.pos << " "<< ans.sum << " " << ans.cnt << " " << ans.val << "\n";
}
}
// cout << ans.sum << " " << ans.cnt << " " << ans.val << "\n";
if (ans.pos < r) {
int sum = getsum(ans.pos + 1, r);
int mn = getmin(ans.pos + 1, r) - suf[r + 1];
ans.val = min(0, min(ans.val + sum, mn));
}
if (a[l] == -1) ans.cnt ++;
if (ans.cnt == (r-l+1)) ans.val = 0;
printf("%d\n", ans.cnt + (-ans.val));
}
//#define bd
int main() {
#ifdef bd
fi("2.txt");
#endif
int n = read(), Q = read();
scanf("%s",s + 1);
for (int i=1; i<=n; ++i)
a[i] = s[i] == '0' ? 1 : - 1;
// if (n <= 1000) { solve1(n, Q); return 0; }
memset(st, 0x3f, sizeof(st));
for (int i=n; i>=1; --i) suf[i] = suf[i + 1] + a[i], st[i][0] = suf[i];
for (int j=1; j<=19; ++j)
for (int i=1; (i+(1<<(j-1)))<=n; ++i) {
st[i][j] = min(st[i][j - 1], st[i + (1<<(j-1))][j - 1]);
}
int top = 1;sk[top] = n + 1;
for (int p,i=n; i>=1; --i) {
if (s[i] == '0') {
if (top > 1) top --;
}
p = sk[top];
f[i][0].pos = p, f[i][0].sum = getsum(i + 1, p - 1), f[i][0].val = getmin(i + 1, p - 1) - suf[p];
f[i][0].cnt = 1;
// cout << p << " " << f[i][0].sum << " " << f[i][0].val << " \n";
if (s[i] == '1') sk[++top] = i;
}
for (int j=0; j<=19; ++j) f[n+1][j].pos = n + 1;
for (int j=1; j<=19; ++j)
for (int i=1; i<=n; ++i) {
f[i][j] = f[i][j - 1] + f[f[i][j-1].pos][j - 1];
// cout << i << " " << f[i][j].pos << "\n";
// cout << f[i][j].val << " " << f[i][j].sum << "\n";
}
while (Q--) work();
return 0;
}
/*
11 1
01110100111
*/
对拍数据生成器
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
#include<cctype>
#include<set>
#include<vector>
#include<queue>
#include<map>
#define fi(s) freopen(s,"r",stdin);
#define fo(s) freopen(s,"w",stdout);
#include<ctime>
using namespace std;
typedef long long LL;
inline int read() {
int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
}
int a[200005];
int main() {
srand(time(0));
int n = 997, q = 1000;
cout << n << " " << q << "\n";
int t = rand() % n + 1;
for (int i=1; i<=t; ++i) a[i] = 1;
random_shuffle(a + 1, a + n + 1);
for (int i=1; i<=n; ++i) cout << a[i];
puts("");
for (int i=1; i<=q; ++i) {
int l = rand() % n + 1;
int r = rand() % (n - l + 1) + l;
cout << l << " " << r << "\n";
}
return 0;
}