2022.07.13 暑假集训 个人排位赛(八)
赛后反省
中规中矩,感觉现在还是开始慢慢刷一刷动态规划了。两道动态规划没有做出。
Problem A
出处
Codeforces-1153D
题解
树形DP。
每一个父节点都是根据子节点的情况去获得的,相当于一个从叶子结点开始向上传递的过程。设dp[i]代表的是以i为根的子树中,i只能够取叶子结点中的第dp[i]大的点。
那么,当i是叶子结点的时候,显然dp[i]=1;
在其他结点的时候,分两种情况。
- 假如该节点要取子节点的最大值,那么 d p [ u ] = m i n { d p [ v ] } v 是 u 的 子 节 点 dp[u]=min\{dp[v]\}\quad v是u的子节点 dp[u]=min{dp[v]}v是u的子节点。因为要取最大的点,那么当然要去子节点中能够取得的最大的数。
- 假如该节点要取子节点的最小值,那么 d p [ u ] = ∑ d p [ v ] v 是 u 的 子 节 点 dp[u]=\sum{dp[v]}\quad v是u的子节点 dp[u]=∑dp[v]v是u的子节点。思考一下,因为要去最小的点,也就是取下面子节点中能取到的数的最小值。假设左子树只能取第2大的数,右子树只能取第4大的数,那么假设两棵树合并之后,父节点只能够去两者的最小值,那么也就是第6大的数了。
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
const int N = 3e5 + 5;
int n, T = 1;
int a[N];
vector<int> ve[N];
int dp[N], k;
void dfs(int u) {
if (!ve[u].size()) {
dp[u] = 1;
k++;
return;
}
int minn = INF;
for (auto v : ve[u]) {
dfs(v);
minn = min(dp[v], minn);
dp[u] += dp[v];
}
if (a[u]) dp[u] = minn;
return;
}
void ready()
{
cin >> n;
ffor(i, 1, n) cin >> a[i];
ffor(i, 2, n) {
int v = i, u;
cin >> u;
ve[u].push_back(v);
}
dfs(1);
int ans = k - dp[1] + 1;
cout << ans;
return;
}
void work()
{
}
signed main()
{
IOS;
// cin>>T;
while (T--) {
ready();
work();
}
return 0;
}
Problem B
出处
Codeforces-452B
题解
选三个点,使得线段距离最短。
那么根据样例2,一条直线的提示,我们也可以对斜对角先进行类似的操作。
然后整合一下,对于矩形来说,只在以下两种情况中选择一种就是最大值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QnPQbY8o-1657725805385)(C:\Users\k’y\AppData\Roaming\Typora\typora-user-images\image-20220713230113490.png)]
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
int T=1;
double n,m;
void ready()
{
cin>>n>>m;
if(n==0){
cout<<n<<' '<<1<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<n<<' '<<0<<'\n';
cout<<n<<' '<<m-1<<'\n';
return;
}
if(m==0){
cout<<1<<' '<<m<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<0<<' '<<m<<'\n';
cout<<n-1<<' '<<m<<'\n';
return;
}/*
if(n<=2){
cout<<0<<' '<<0<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<n<<' '<<0<<'\n';
cout<<0<<' '<<m<<'\n';
return;
}
if(m<=2){
cout<<0<<' '<<0<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<0<<' '<<m<<'\n';
cout<<n<<' '<<0<<'\n';
return;
}*/
if(n>=m){
double ans1=2*sqrt(n*n+(m-1)*(m-1))+sqrt(n*n+m*m);
double ans2=2*sqrt(n*n+m*m)+n;
if(ans1>=ans2){
cout<<0<<' '<<1<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<0<<' '<<0<<'\n';
cout<<n<<' '<<m-1<<'\n';
}
else{
cout<<0<<' '<<0<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<0<<' '<<m<<'\n';
cout<<n<<' '<<0<<'\n';
}
return ;
}
else{
double ans1=2*sqrt(m*m+(n-1)*(n-1))+sqrt(n*n+m*m);
double ans2=2*sqrt(n*n+m*m)+m;
if(ans1>=ans2){
cout<<1<<' '<<0<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<0<<' '<<0<<'\n';
cout<<n-1<<' '<<m<<'\n';
}
else{
cout<<0<<' '<<0<<'\n';
cout<<n<<' '<<m<<'\n';
cout<<n<<' '<<0<<'\n';
cout<<0<<' '<<m<<'\n';
}
}
}
void work()
{
}
signed main()
{
IOS;
ready();
//cin>>T;
while (T--) {
work();
}
return 0;
}
Problem C
出处
Codeforces-229D
题解
DP
dp[i]为当以i结尾时达到满足条件的状态需要多少步操作。las[i]为当以i为结尾并且能满足条件时该序列最后一个数是多少。
枚举i之前的j,如果区间[j+1,i]之间的和大于las[j],则说明这是一种满足条件的变换,区间[j+1,i]合并的次数为i-j-1。那么在这个情况下所需要的操作次数即为dp[j]+i-j-1。如果dp[i]>=dp[j]+i-j-1,即可更新。
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
const int N = 5005;
int n, T = 1;
int sum[N], a[N];
int las[N], dp[N];
void ready()
{
cin >> n;
ffor(i, 1, n) {
cin >> a[i];
sum[i] = sum[i - 1] + a[i];
dp[i] = las[i] = INF;
}
dp[1] = 0; las[1] = a[1];
}
void out()
{
cout << " dp \n";
ffor(i, 1, n) cout << dp[i] << ' ';
cout << " \n las \n";
ffor(i, 1, n) cout << las[i] << ' ';
cout << '\n';
cout << '\n';
}
void work()
{
ffor(i, 2, n) {
ffor(j, 0, i - 1) {
if (las[j] <= sum[i] - sum[j] && dp[i] >= dp[j] + i - j - 1) {
las[i] = sum[i] - sum[j];
dp[i] = dp[j] + i - j - 1;
}
}
//out();
//cout << " i = " << i << " dp = " << dp[i] << " las = " << las[i] << '\n';
}
cout << dp[n];
}
signed main()
{
IOS;
// cin>>T;
while (T--) {
ready();
work();
}
return 0;
}
Problem D
出处
Codeforces-229A
题解
算出每一个位置,离它最近的1在哪里,然后每一列地求和,输出最小即可。
需要注意,如果向右转的话排在最后一个的会去到最前面去。
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
int n, T = 1, m;
int a[105][10005];
int d[105][10005];
void ready()
{
cin>>n>>m;
ffor(i,1,n){
string st;
cin>>st;
ffor(j,0,m-1){
a[i][j+1]=st[j]-'0';
d[i][j+1]=INF;
}
}
ffor(i,1,n){
int di=0;
ffor(j,1,m){
if(a[i][j]) di=j;
if(di) d[i][j]=min(abs(di-j),d[i][j]);
}
//cout<< "i = "<< i << " di = "<<di<<'\n';
if(di)
ffor(j,1,m){
d[i][j]=min(d[i][j],m-di+j);
}
di=0;
rrep(j,m,1){
if(a[i][j]) di=j;
if(di) d[i][j]=min(abs(di-j),d[i][j]);
}
//cout<< "i = "<< i << " di = "<<di<<'\n';
if(di)
rrep(j,m,1){
d[i][j]=min(d[i][j],m-j+di);
}
}
/* ffor(i,1,n){
ffor(j,1,m)
cout<<d[i][j]<<' ';
cout<<'\n';
}*/
int ans=INF;
ffor(j,1,m){
bool ok=true;
int now=0;
ffor(i,1,n){
if(d[i][j]==INF){
ok=false;
break;
}
now+=d[i][j];
//cout<<" i j "<<i<<' '<<j<<' '<<now<<'\n';
}
if(ok) ans=min(ans,now);
}
if(ans==INF) ans=-1;
cout<<ans;
}
void work()
{
}
signed main()
{
IOS;
ready();
//cin>>T;
while (T--) {
work();
}
return 0;
}
Problem E
出处
Codeforces-229B
题解
加限制的最短路问题。
用一个map来维护, m p [ v ] [ t ] mp[v][t] mp[v][t]代表 第v个点在时间t的时候的出发时间为 m p [ v ] [ t ] mp[v][t] mp[v][t]。那么每次在SPFA用节点u对其子节点进行松弛的时候,如果当前 m p [ u ] [ d i s [ u ] ] mp[u][dis[u]] mp[u][dis[u]]是存在值的,那么原本松弛的条件为 d i s [ u ] + c ≤ d i s [ v ] dis[u]+c \le dis[v] dis[u]+c≤dis[v]就变成了 m p [ u ] [ d i s [ u ] ] + c ≤ d i s [ v ] mp[u][dis[u]]+c\le dis[v] mp[u][dis[u]]+c≤dis[v]。后面就是一般的最短路的操作。
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
const int N=1e5+5,M=2e5+5;
int n, T = 1, m;
int pi,p[N],nxt[M],to[M],val[M];
vector<int> ve;
map<int,int> mp[N];
inline void add_in(int u,int v,int c){
pi++;nxt[pi]=p[u];p[u]=pi;to[pi]=v;val[pi]=c;
}
void ready()
{
cin>>n>>m;
ffor(i,1,m){
int u,v,c;
cin>>u>>v>>c;
add_in(u,v,c);
add_in(v,u,c);
}
ffor(i,1,n){
int ki;
cin>>ki;
ffor(j,1,ki){
int tij;
cin>>tij;
ve.push_back(tij);
}
if(ve.empty()) continue;
//sort(ve.begin(),ve.end());
//ve.erase(unique(ve.begin(),ve.end()),ve.end());
int tim=ve.back()+1;
rrep(j,ve.size()-2,0){
if(ve[j]+1!=ve[j+1]){
int now=ve.back();
while(now!=ve[j]){
ve.pop_back();
mp[i][now]=tim;
now=ve.back();
}
tim=ve.back()+1;
}
}
while(ve.size()){
int now=ve.back();
ve.pop_back();
mp[i][now]=tim;
}
}/*
ffor(i,1,n){
cout<<"u = "<<i<<'\n';
for(auto item:mp[i]){
cout<<item.first<<' '<<item.second<<'\n';
}
}
cout<<'\n';*/
}
int dis[N];
bool f[N];
void out(){
for(int i=1;i<=n;i++)
cout<<dis[i]<<' ';
cout<<'\n'<<'\n';
}
void work()
{
ffor(i,1,n) dis[i]=INF;
dis[1]=0;
queue<int> s;
s.push(1);
while(s.size()){
int u=s.front();s.pop();
f[u]=false;
for(int k=p[u];k;k=nxt[k]){
int v=to[k],c=val[k];
int ut=dis[u];
if(mp[u][ut]>0) ut=mp[u][ut];
int vt=ut+c;
if(vt<=dis[v]){
dis[v]=vt;
if(!f[v]){
f[v]=true;
s.push(v);
}
}
}
// out();
}
if(dis[n]==INF) dis[n]=-1;
cout<<dis[n];
}
signed main()
{
IOS;
ready();
//cin>>T;
while (T--) {
work();
}
return 0;
}
/*
4 6
1 2 8
1 3 3
1 4 18
2 3 4
2 4 5
3 4 3
0
1 3
2 3 4
0
*/
Problem H
出处
Codeforces-798D
题解
首先,肯定取越多的数越好,所以取$\left \lfloor \frac{n}{2} \right \rfloor +1 $个数是最好的选择。然后根据a的数值从大到小排序。
如果n是偶数,前两个必选,剩下的两两一组选b值最大的。
如果n是奇数,第一个必选,剩下的两两一组选b值最大的。
贪心正确性,因为每次都在两个里面选最大的一组,那么剩下的所有加起来绝对比已经选择的要小,而且已经选择了的数的个数比没选的多,所以超过总和的一半。
代码
// Good Good Study, Day Day AC.
#include <iostream>
#include <stdio.h>
#include <cstdio>
#include <stdlib.h>
#include <string>
#include <string.h>
#include <cstring>
#include <math.h>
#include <cmath>
#include <queue>
#include <deque>
#include <stack>
#include <vector>
#include <map>
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
#define ffor(i,a,b) for(int i=(a) ;i<=(b) ;i++)
#define rrep(i,a,b) for(int i=(a) ;i>=(b) ;i--)
#define mst(v,s) memset(v,s,sizeof(v))
#define IOS ios::sync_with_stdio(false),cin.tie(0)
#define ll long long
#define INF 0x7f7f7f7f7f7f7f7f
#define inf 0x7f7f7f7f
#define PII pair<int,int>
#define int long long
using namespace std;
int n, T = 1, m;
int alla, allb;
const int N = 1e5 + 5;
struct GGG {
int a, b, id;
}t[N];
bool cmp(GGG i, GGG j) {
return i.a > j.a;
}
void ready()
{
cin >> n;
ffor(i, 1, n) cin >> t[i].a;
ffor(i, 1, n) cin >> t[i].b;
ffor(i, 1, n) t[i].id = i;
sort(t + 1, t + n + 1, cmp);
}
void work()
{
vector<int> ans;
if (n % 2) {
ans.push_back(1);
for (int i = 2; i <= n; i += 2) {
if (t[i].b > t[i + 1].b) ans.push_back(i);
else ans.push_back(i + 1);
}
}
else {
ans.push_back(1); ans.push_back(2);
for (int i = 3; i <= n; i += 2) {
if (t[i].b > t[i + 1].b) ans.push_back(i);
else ans.push_back(i + 1);
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << '\n';
for (auto item : ans) {
cout << t[item].id << ' ';
}
}
signed main()
{
IOS;
ready();
//cin>>T;
while (T--) {
work();
}
return 0;
}
n) t[i].id = i;
sort(t + 1, t + n + 1, cmp);
}
void work()
{
vector ans;
if (n % 2) {
ans.push_back(1);
for (int i = 2; i <= n; i += 2) {
if (t[i].b > t[i + 1].b) ans.push_back(i);
else ans.push_back(i + 1);
}
}
else {
ans.push_back(1); ans.push_back(2);
for (int i = 3; i <= n; i += 2) {
if (t[i].b > t[i + 1].b) ans.push_back(i);
else ans.push_back(i + 1);
}
}
sort(ans.begin(), ans.end());
cout << ans.size() << ‘\n’;
for (auto item : ans) {
cout << t[item].id << ’ ';
}
}
signed main()
{
IOS;
ready();
//cin>>T;
while (T–) {
work();
}
return 0;
}