BOI 的题目真有趣,又卡空间又卡时间…
真的很不习惯啊…
Part 1 总结
期望得分:
50
+
100
+
30
+
20
=
200
50+100+30+20=200
50+100+30+20=200。
实际得分:
50
+
70
+
[
50
,
70
]
+
20
=
[
190
,
210
]
50+70+[50,70]+20=[190,210]
50+70+[50,70]+20=[190,210]
(大概是,T3T4没测,大概率没挂。因为 T2 不想卡空间,觉得要垫底,没交。)
可能是我自己本身就存在的毛病吧…看到四道题空间都卡在 64MB-128MB
就觉得恶心…再加上如此自闭的题目难度…
T1 由于是最后 20 min 才想到,没有来得及调出输出方案的部分。
T3 实际暴力剪枝得分是
50
−
80
50-80
50−80 分,T2 解法没有错误也没有被卡空间,还在 LOJ 上过了,但在学校的老爷机上显然只有
70
70
70 分。
这四道题目其实有一个非常明显的共性就是四道题目都有一个比较难察觉的性质,只要想到这个性质,这道题目也就变的非常容易了。
Part 2 题解
tip:如果想看题面请跳到 Part 4。
T1-糖果机器(candy)
原题:LOJ2858「BalticOI 2009 Day1」糖果机器
dilworth定理裸题…比较偏门的一道题目…谁还记得到啊…
算法1:60%
考虑两个糖果
(
i
,
j
)
(i,j)
(i,j) 是否能够被同一个机器人接住,通过画图我们容易发现充要条件是:
s
i
+
t
i
≤
s
j
+
t
j
s
i
−
t
i
≥
s
j
−
t
j
s_i+t_i\leq s_j+t_j\\ s_i-t_i≥s_j-t_j\\
si+ti≤sj+tjsi−ti≥sj−tj
我们可以把所有满足这样关系的
(
i
,
j
)
(i,j)
(i,j) 连边,然后建图。
容易发现这就是一个最小不相交路径覆盖问题,我们可以运用二分图解决。
复杂度:
O
(
n
2
)
O(n^2)
O(n2)。(很卡空间)
算法2:100%
运用 dilworth 定理,偏序问题的最小不相交路径覆盖等于最长反链,我们只需要求出最长反链即可。而求最长反链,即满足上面的条件有且只有其中一个不成立的最长序列(不重有序)。
经典做法就是将机器人按照
s
i
+
t
i
s_i+t_i
si+ti 递增的关系排序,这样我们就只可能是第二个条件不满足,而不满足这个关系的最长序列就是我们的最长上升子序列。
复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
T2-手套(gloves)
原题:LOJ2366「BalticOI 2008」手套
这题部分分就跟没有一样。
将这个问题想象为一个博弈问题,你只能够指明你在左手套中拿了
x
x
x 个手套,在右手套中拿了
y
y
y 个手套,对方会尽量让你不会成功拿出一对左右手套。
首先显然的是
a
i
,
b
i
a_i,b_i
ai,bi 中存在一个为零的颜色对方一定会让你拿,因此我们可以将这种情况直接计入答案。
对于
a
i
,
b
i
a_i,b_i
ai,bi 都大于零的颜色,我们只能够指定取的数量,那么对方一定会将你往最坏的情况下带。
我们枚举子集
S
S
S 表示你现在指明你在左手套中拿了
x
x
x 个手套,如果对方让你拥有的左手套颜色集合为
S
S
S,那么此时对方能让你取的最多的右手套数就是
∑
i
∉
S
b
i
+
1
\sum_{i\notin S}b_i+1
∑i∈/Sbi+1,此时一定有
x
∈
[
∣
S
∣
,
∑
i
∈
S
a
i
]
x \in [|S|,\sum_{i\in S}a_i]
x∈[∣S∣,∑i∈Sai] 。
对方一定会挑右手套数最大的
S
S
S,那么这就相当于我们每个集合
S
S
S 都会对区间
[
∣
S
∣
,
∑
i
∈
S
a
i
]
[|S|,\sum_{i\in S}a_i]
[∣S∣,∑i∈Sai] 做一个取
max
\max
max 操作,我们在这个序列上找到
x
+
a
x
x+a_x
x+ax 最大的二元组。
显然这个东西一个线段树就可以搞定,但是常数过大。
考虑
S
S
S 的一个子集
S
′
S'
S′ 且满足
∣
S
′
∣
+
1
=
∣
S
∣
|S'|+1=|S|
∣S′∣+1=∣S∣ ,显然有
∑
i
∉
S
′
b
i
+
1
≥
∑
i
∉
S
b
i
+
1
\sum_{i\notin S'}b_i+1≥\sum_{i\notin S}b_i+1
∑i∈/S′bi+1≥∑i∈/Sbi+1 而
S
′
S'
S′ 的右端点比
S
S
S 小
1
1
1 ,这意味这我们的右端点其实可以直接忽略。
这样这个问题就简单了,我们显然可以直接使用单调栈来进行维护。而我们维护的呈现在坐标轴上其实就是一个单调递减的阶梯状函数,答案很容易得出。
复杂度:
O
(
2
n
n
)
O(2^nn)
O(2nn)。
T3-甲虫(beetle)
原题:LOJ2857「BalticOI 2009 Day1」甲虫
其实是一个"费用提前计算"的经典问题。
一个显然的性质就是甲虫一定会往
0
0
0 的右边走一段距离,然后掉头往
0
0
0 的左边走一段距离,如此循环往复。
显然考虑区间 dp,定义
f
l
,
r
,
0
/
1
f_{l,r,0/1}
fl,r,0/1 表示现在甲虫已经走过了
[
l
,
r
]
[l,r]
[l,r] 中的所有点,现在位于
x
l
/
x
r
x_l/ x_r
xl/xr 的最小水浪费量。
但是我们发现接下来的费用与我们先前走过的距离有关,这意味这我们计算时需要知道我们走到这个点所需距离,而这个距离显然无法存储。
我们利用费用提前计算的思想,在每次走过一段路就计入接下来的路途会因此浪费的水量,但这需要我们知道我们最终喝了多少滴露水。
本题数据范围不大,我们只需要枚举我们最终喝了多少滴露水,每次动态规划处理一下边界即可。
注意到可能存在水滴水量变为负数的情况,但这一定不优,可以不考虑。
复杂度:
O
(
n
3
)
O(n^3)
O(n3)。
T4-选举(elections)
原题:LOJ2710「BalkanOI 2018 Day1」Election
一看就是一个猜结论题…
将 C
置为 1
,T
置为 -1
答案就是这一段区间的和减去这一段区间的最大子段和。
这东西随便维护一下即可。
复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)。
证明这篇博客已经写的很好了(确实不是很想写了)。
Part 3 实现
T1-candy
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 100000
#define INF 1000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n,len,cnt;Pr p[MAXN+5];
struct node{
int s,t,x,y,id,res;
}a[MAXN+5];
bool cmp(node s1,node s2){
if(s1.x==s2.x)return s1.y>s2.y;
return s1.x<s2.x;
}
int main(){
freopen("candy.in","r",stdin);
freopen("candy.out","w",stdout);
n=read();
for(int i=1;i<=n;i++){
a[i].s=read(),a[i].t=read();
a[i].x=a[i].s+a[i].t,a[i].y=a[i].s-a[i].t;
a[i].id=i;
}
sort(a+1,a+n+1,cmp);
for(int i=1;i<=n;i++){
int x=lower_bound(p+1,p+len+1,Pr(a[i].y,0))-p;
if(x>len)a[i].res=++cnt,p[++len]=Pr(a[i].y,cnt);
else{
a[i].res=p[x].Y;
p[x]=Pr(a[i].y,p[x].Y);
}
}
printf("%d\n",len);
for(int i=1;i<=n;i++)
printf("%d %d %d\n",a[i].s,a[i].t,a[i].res);
}
T2-gloves
考场解法
线段树跑的极慢…
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
using namespace std;
#define LL long long
#define Pr pair<LL,LL>
#define X first
#define Y second
#define MAXN 20
#define MAXM (1<<20)
#define INF 2000000000
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n,m,a[MAXN+5],b[MAXN+5],tX,tY,tot,cnt;
int ta[MAXN+5],tb[MAXN+5],mr;
int Q[MAXM+5],L,R;Pr ans;
struct node{
int l,r,v;
}p[MAXM+5];
int val[(MAXM<<1)+5];
bool cmp(node s1,node s2){return s1.l<s2.l;}
struct Seg_tree{
#define mid (l+r>>1)
#define pl (x<<1)
#define pr (x<<1|1)
int mx[(MAXM<<2)+5],tag[(MAXM<<2)+5];
void pushdown(int x){
if(tag[x]){
mx[pl]=max(mx[pl],tag[x]),tag[pl]=max(tag[pl],tag[x]);
mx[pr]=max(mx[pr],tag[x]),tag[pr]=max(tag[pr],tag[x]);
tag[x]=0;
}
}
void Insert(int x,int l,int r,int L,int R,int pv){
if(r<L||R<l)return ;
if(L<=l&&r<=R){mx[x]=max(mx[x],pv),tag[x]=max(tag[x],pv);return ;}
pushdown(x);
Insert(pl,l,mid,L,R,pv),Insert(pr,mid+1,r,L,R,pv);
mx[x]=max(mx[pl],mx[pr]);
}
void Query(int x,int l,int r){
if(l==r){
if(ans.X+ans.Y==val[l]+tX+mx[x]+tY)ans=min(ans,Pr(val[l]+tX,mx[x]+tY));
else if(ans.X+ans.Y>val[l]+tX+mx[x]+tY)ans=Pr(val[l]+tX,mx[x]+tY);
return ;
}pushdown(x);
Query(pl,l,mid),Query(pr,mid+1,r);
}
}T;
int main(){
freopen("gloves.in","r",stdin);
freopen("gloves.out","w",stdout);
n=read(),ans=Pr(INF,INF);
for(int i=0;i<n;i++)a[i]=read();
for(int i=0;i<n;i++)b[i]=read();
for(int i=0;i<n;i++){
if(!a[i])tY+=b[i];
else if(!b[i])tX+=a[i];
else mr+=a[i],ta[m]=a[i],tb[m++]=b[i];
}
for(int S=1;S<(1<<m);S++){
int cnt=0,sa=0,sb=0;
for(int i=0;i<m;i++)
if(S&(1<<i))sa+=ta[i],cnt++;
else sb+=tb[i];
p[++tot]=(node){cnt,sa,sb+1};
}
sort(p+1,p+tot+1,cmp);
for(int i=1;i<=tot;i++){
val[++cnt]=p[i].l,val[++cnt]=p[i].r;
if(p[i].r<mr)val[++cnt]=p[i].r+1;
}
sort(val+1,val+cnt+1);
cnt=unique(val+1,val+cnt+1)-val-1;
for(int i=1;i<=tot;i++){
p[i].l=lower_bound(val+1,val+cnt+1,p[i].l)-val;
p[i].r=lower_bound(val+1,val+cnt+1,p[i].r)-val;
T.Insert(1,1,cnt,p[i].l,p[i].r,p[i].v);
}
T.Query(1,1,cnt);
printf("%d\n%d\n",ans.X,ans.Y);
}
正解
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 20
#define MAXM (1<<20)
#define INF 2000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int tot,n,m,a[MAXN+5],b[MAXN+5],pa[MAXN+5],pb[MAXN+5],tX,tY,tp;
int mx;Pr ans;
struct node{
int x,y;
}p[MAXM+5],stk[MAXM+5];
bool cmp(node s1,node s2){
if(s1.x==s2.x)return s1.y>s2.y;
return s1.x<s2.x;
}
int main(){
freopen("gloves.in","r",stdin);
freopen("gloves.out","w",stdout);
n=read(),mx=INF;
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1;i<=n;i++)b[i]=read();
for(int i=1;i<=n;i++){
if(!a[i])tY+=b[i];
else if(!b[i])tX+=a[i];
else pa[m]=a[i],pb[m++]=b[i];
}
tot=(1<<m);
for(int S=1;S<=(1<<m);S++){
int sa=0,sb=0;
for(int i=0;i<m;i++)
if(S&(1<<i))sa+=pa[i];
else sb+=pb[i];
p[S]=(node){sa,sb};
}
sort(p+1,p+tot+1,cmp),p[0].x=-233;
for(int i=1;i<=tot;i++){
if(p[i].x==p[i-1].x)continue;
while(tp&&stk[tp].y<=p[i].y)tp--;
stk[++tp]=p[i];
}
for(int i=2;i<=tp;i++)
if(stk[i-1].x+stk[i].y+2<mx)mx=stk[i-1].x+stk[i].y+2,ans=Pr(stk[i-1].x+1,stk[i].y+1);
printf("%d\n%d\n",ans.X+tX,ans.Y+tY);
}
T3-beetle
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 300
#define INF 2000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n,m,k,t,x[MAXN+5];
int vis[MAXN+5][MAXN+5][2],f[MAXN+5][MAXN+5][2];
int dfs(int l,int r,int op){
if(r-l==k)return 0;
if(vis[l][r][op]==k)return f[l][r][op];
vis[l][r][op]=k;
int ans=INF,p=(op?r:l);
if(l!=1)ans=min(ans,dfs(l-1,r,0)+(x[p]-x[l-1])*(k-(r-l)));
if(r!=n)ans=min(ans,dfs(l,r+1,1)+(x[r+1]-x[p])*(k-(r-l)));
return f[l][r][op]=ans;
}
int main(){
freopen("beetle.in","r",stdin);
freopen("beetle.out","w",stdout);
n=read(),m=read();
for(int i=1;i<=n;i++)x[i]=read();
x[++n]=0;
sort(x+1,x+n+1);
int ans=0;
for(int i=1;i<=n;i++)if(x[i]==0){t=i;break;}
for(k=1;k<n;k++)ans=max(ans,k*m-dfs(t,t,0));
printf("%d\n",ans);
}
T4-elections
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define Pr pair<int,int>
#define X first
#define Y second
#define MAXN 500000
#define INF 1000000000
#define MOD 1000000007
#define mem(x,v) memset(x,v,sizeof(x))
LL read(){
LL x=0,F=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')F=-1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return x*F;
}
int n,q,sum[MAXN+5];char s[MAXN+5];
struct Seg_tree{
#define pl (x<<1)
#define pr (x<<1|1)
#define mid (l+r>>1)
struct node{
int ls,rs,ms;
node operator +(const node &s1)const{
return (node){min(ls,s1.ls),max(rs,s1.rs),max(max(ms,s1.ms),s1.rs-ls)};
}
}p[(MAXN<<2)+5];
void Build(int x,int l,int r){
if(l==r){
p[x]=(node){sum[l],sum[l],0};
return ;
}
Build(pl,l,mid),Build(pr,mid+1,r);
p[x]=p[pl]+p[pr];
}
node Query(int x,int l,int r,int L,int R){
if(L<=l&&r<=R)return p[x];
if(R<=mid)return Query(pl,l,mid,L,R);
else if(L>mid)return Query(pr,mid+1,r,L,R);
else return Query(pl,l,mid,L,R)+Query(pr,mid+1,r,L,R);
}
}T;
int main(){
freopen("elections.in","r",stdin);
freopen("elections.out","w",stdout);
n=read();
scanf("%s",s+1);
for(int i=1;i<=n;i++)sum[i]=sum[i-1]+(s[i]=='C'?1:-1);
T.Build(1,0,n);
q=read();
while(q--){
int l=read(),r=read();
printf("%d\n",T.Query(1,0,n,l-1,r).ms-(sum[r]-sum[l-1]));
}
}
Part 4 resource
T1
T2
T3
T4