Codeforcs 每日一练 678C+527C+1012C

678C Joty and Chocolate

传送门
镜像
1600 数论
题意:n块瓷砖从1n编号,Joty可以将其中编号为a的倍数的瓷砖涂成红色,编号为b的倍数的瓷砖涂成蓝色,每涂一块红色的瓷砖可以得到p块巧克力,而蓝色的瓷砖则提供q块巧克力,最大化Joty能够得到的巧克力数。
水题
,假设x块编号是a的倍数,y块编号是b的倍数,z块编号是a和b最小公倍数的倍数,当p>q的情况下,则x+z块涂成红色,y-z块涂成蓝色,其他情况同理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll n,a,b,p,q;
ll gcd(ll x,ll y){if(y==0)return x;return gcd(y,x%y);}
int main()
{
IOS
cin>>n>>a>>b>>p>>q;
ll m=a*b/gcd(a,b);
m=n/m;
a=n/a-m;
b=n/b-m;
ll ans;
if(p>=q)ans=(a+m)*p+b*q;
else ans=(b+m)*q+a*p;
cout<<ans;
return 0;
}

527C Glass Carving

传送门
镜像
1800 数据结构
题意:一个长h宽w的玻璃砖,每次垂直一条边切割,问每次切割后的最大面积
开两个set和两个multiset就可以解出来了,两个set分别记录x轴和y轴上面的切割点(初始就分别扔进去0,w和0,h),multiset则记录x轴或y轴上切割产生的线段,对于每次切割后,找出当前切割点两端最近的点,然后在multiset里把这段的长度erase掉,把新生成的两段插进去,最后输出两个multiset里最大值的乘积即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
ll n,w,h;
set<ll> x,y;
multiset<ll> a,b;
int main()
{
IOS
cin>>w>>h>>n;
y.insert(h);y.insert(0);
x.insert(w);x.insert(0);
a.insert(h);
b.insert(w);
while(n--){
char s;
ll l;
cin>>s>>l;
if(s=='H'){
auto i=y.lower_bound(l);
if(*i>l)i--;
auto j=y.upper_bound(l);
ll tmp=*i-*j;
auto m=a.lower_bound(*j-*i);
a.erase(m);
a.insert(l-*i);
a.insert(*j-l);
y.insert(l);
}else{
auto i=x.lower_bound(l);
if(*i>l)i--;
auto j=x.upper_bound(l);
ll tmp=*i-*j;
auto m=b.lower_bound(*j-*i);
b.erase(m);
b.insert(l-*i);
b.insert(*j-l);
x.insert(l);
}
auto p=a.end(),q=b.end();
q--;
p--;
cout<<(*p)*(*q)<<endl;
}
return 0;
}

小姐姐的平衡树做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<iostream>
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
#include<ext/pb_ds/hash_policy.hpp>
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> x,y;
tree<pair<int,int>,null_type,greater<pair<int,int>>,rb_tree_tag,tree_order_statistics_node_update> xx,yy;
cc_hash_table<int,int> hx,hy;
int w,h,n;
int main()
{
ios::sync_with_stdio(false);
cin>>w>>h>>n;
x.insert(0);
x.insert(w);
y.insert(0);
y.insert(h);
xx.insert(make_pair(w,++hx[w]));
yy.insert(make_pair(h,++hy[h]));
for(int i=0;i<n;i++)
{
char c;
int num;
cin>>c>>num;
if(c=='V')
{
int rank=x.order_of_key(num);
int pre=*x.find_by_order(rank-1);
int nxt=*x.find_by_order(rank);
xx.erase(make_pair(nxt-pre,hx[nxt-pre]--));
xx.insert(make_pair(num-pre,++hx[num-pre]));
xx.insert(make_pair(nxt-num,++hx[nxt-num]));
x.insert(num);
}
else if(c=='H')
{
int rank=y.order_of_key(num);
int pre=*y.find_by_order(rank-1);
int nxt=*y.find_by_order(rank);
yy.erase(make_pair(nxt-pre,hy[nxt-pre]--));
yy.insert(make_pair(num-pre,++hy[num-pre]));
yy.insert(make_pair(nxt-num,++hy[nxt-num]));
y.insert(num);
}
cout<<(ll)xx.find_by_order(0)->first*(ll)yy.find_by_order(0)->first<<endl;
}
return 0;
}

1012C Hills

传送门
镜像
2000 dp
题意:一共n座山,每花费1h可以使一座山降低1的高度,最少花费多少时间可以使存在k座山的高度严格大于两侧的山的高度。输出k从1~$\lceil \frac{n}{2} \rceil$的结果。
一开始想的做法是dp[i][j][0]代表前i个有j个满足,并且最后一个不取,dp[i][j][1]代表前i个有j个满足,并且最后一个取,但是状态转移尬住了,因为还要考虑倒数第二个的状态,那开三维就好了,dp[i][j][0]代表前i个有j个满足,并且最后两个都不取,dp[i][j][2]代表前i个有j个满足,并且取最后一个,dp[i][j][1]代表前i个有j个满足,并且取倒数第二个。
那么状态转移方程就很好推了:

1
2
3
dp[i][j][1]=dp[i-1][j][2]+max(0,a[i]-a[i-1]+1)
dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1])
dp[i][j][2]=min(dp[i-1][j-1][0]+max(0,a[i-1]-a[i]+1),dp[i-1][j-1][1]+max(0,min(a[i-1],a[i-2]-1)-a[i]+1))

然后就是初始化,不确定的和非法状态存为极大值,显然dp[1][1][2]=0,并且dp[i][0][0]=0(1<=i<=n)从2开始转移就可以A了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include<bits/stdc++.h>
using namespace std;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define maxn 5005
int n,a[maxn],dp[maxn][maxn][3];
int main()
{
IOS
cin>>n;
for (int i = 1; i <=n ; ++i) {
cin>>a[i];
for (int j = 0; j <=n ; ++j) {
for (int k = 0; k <=2 ; ++k) {
dp[i][j][k]=2146870000;
}
}
dp[i][0][0]=0;
}
dp[1][1][2]=0;
for (int i = 2; i <=n ; ++i) {
for (int j = 1; j <=(i+1)/2 ; ++j) {
dp[i][j][2]=min(dp[i-1][j-1][0]+max(0,a[i-1]-a[i]+1),dp[i-1][j-1][1]+max(0,min(a[i-1],a[i-2]-1)-a[i]+1));
dp[i][j][0]=min(dp[i-1][j][0],dp[i-1][j][1]);
dp[i][j][1]=dp[i-1][j][2]+max(0,a[i]-a[i-1]+1);
}
}
for (int k = 1; k <=(n+1)/2 ; ++k) {
cout<<min(dp[n][k][2],min(dp[n][k][0],dp[n][k][1]))<<" ";
}
return 0;
}