ICPC2020南京站

C.Certain Scientific Railgun

这道题算是个挺巧妙的构造题吧,可以对原图进行若干次对称变换,这样就可以固定住决策,从而极大地简化了代码量 然而简化之后还是很折磨

决策分为两种情况,首先只向左走,然后再上下;或者向左走后再向右,然后上下走。

按照题解的做法维护四个set就行了接下来这道题需要的就只是亿点点细节

注意一下,有可能最优解只是上下走,要注意特判,或者交换一下X,Y轴再算一次。我这里的做法是增加了一个坐标为(0,0)(0,0)的点,这样能保证在X轴上能“移动一次”(即使这时候的花费为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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
typedef pair<ll,int>pli;
typedef double db;
const int maxn=1e5+5;
const ll inf=1e18;
const ll mod=998244353;
const double eps=1e-10;
int n;
ll ans=1e18;
struct node{
int x,y;
}a[maxn],b[maxn];
unordered_map<int,int>mp1,mp2,in;
unordered_map<int,ll>sum;
set<pli>s1,s2,s3,s4;
void init(){
mp1.clear();
mp2.clear();
in.clear();
s1.clear();
s2.clear();
s3.clear();
s4.clear();
sum.clear();
}
bool cmp(node x,node y){
return x.x<y.x;
}
void solve(){
init();
// ll res=0;
sort(b+1,b+n+1,cmp);
for(int i=1;i<=n;i++){
int x=b[i].x,y=b[i].y;
mp1[x]=max(mp1[x],y);
mp2[x]=min(mp2[x],y);
}
int up=0,dn=0;
int mn=0,mx=0;
for(int i=n;i>=1;i--){
if(b[i].x<=0)break;
up=max(up,b[i].y);
dn=min(dn,b[i].y);
}
for(int i=1;i<=n;i++){
if(b[i].x>0)break;
if(i!=1&&b[i-1].x==b[i].x)continue;
if(i!=1){
mx=max(mx,mp1[b[i-1].x]);
mn=min(mn,mp2[b[i-1].x]);
}
ans=min(ans,1ll*abs(b[i].x)+2*max(mx,up)+abs(min(mn,dn)));
}
mn=0,mx=0;
for(int i=n;i>=1;i--){
if(b[i].x<=0)break;
if(i!=n&&b[i+1].x==b[i].x)continue;
if(i!=n){
mx=max(mx,mp1[b[i+1].x]);
mn=min(mn,mp2[b[i+1].x]);
}
ll temp=1ll*b[i].x+2*mx+abs(mn);
sum[b[i].x]=temp;
if(mx==0){
if(mn==0)s4.insert({temp,b[i].x}),in[b[i].x]=4;
else s2.insert({temp,b[i].x}),in[b[i].x]=2;
}
else {
if(mn==0)s1.insert({temp,b[i].x}),in[b[i].x]=1;
else s3.insert({temp,b[i].x}),in[b[i].x]=3;
}
}
mn=0,mx=0;
up=0,dn=0;
ll res1=inf,res2=inf,res3=inf,res4=inf;
int pos1=n,pos2=n;
for(int i=1;i<=n;i++){
if(b[i].x>=0)break;
if(i!=1&&b[i].x==b[i-1].x)continue;
if(i>1){
mx=max(mx,mp1[b[i-1].x]);
mn=min(mn,mp2[b[i-1].x]);
}
while(b[pos1].x>0&&mx>=up){
int cur=b[pos1].x;
if(in[b[pos1].x]==1){
in[b[pos1].x]=4;
s1.erase({sum[b[pos1].x],b[pos1].x});
sum[b[pos1].x]=b[pos1].x;
s4.insert({sum[b[pos1].x],b[pos1].x});
}
else if(in[b[pos1].x]==3){
in[b[pos1].x]=2;
s3.erase({sum[b[pos1].x],b[pos1].x});
sum[b[pos1].x]-=2*up;
s2.insert({sum[b[pos1].x],b[pos1].x});
}
up=max(up,mp1[b[pos1].x]);
while(b[pos1].x==cur)pos1--;
}
while(b[pos2].x>0&&mn<=dn){
int cur=b[pos2].x;
if(in[cur]==2){
in[cur]=4;
s2.erase({sum[cur],cur});
sum[cur]=cur;
s4.insert({sum[cur],cur});
}
else if(in[cur]==3){
in[cur]=1;
s3.erase({sum[cur],cur});
sum[cur]-=abs(dn);
s1.insert({sum[cur],cur});
}
dn=min(dn,mp2[b[pos2].x]);
while(b[pos2].x==cur)pos2--;
}
if(!s1.empty())res1=(*s1.begin()).first+abs(mn)+abs(b[i].x)*2;
if(!s2.empty())res2=(*s2.begin()).first+2*mx+abs(b[i].x)*2;
if(!s3.empty())res3=(*s3.begin()).first+abs(b[i].x)*2;
if(!s4.empty())res4=(*s4.begin()).first+abs(mn)+2*mx+abs(b[i].x)*2;
ans=min(ans,min(min(res1,res2),min(res3,res4)));
// printf("%lld %lld %lld %lld\n",res1,res2,res3,res4);
}
}

int main(){
int T=1;scanf("%d",&T);
while(T--){
ans=1e18;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int x,y;
scanf("%d%d",&x,&y);
a[i]={x,y};
}
a[n+1]={0,0};n++;
for(int i=1;i<=n;i++)b[i]=a[i];
solve();
for(int i=1;i<=n;i++)b[i]={-a[i].x,a[i].y};
solve();
for(int i=1;i<=n;i++)b[i]={-a[i].x,-a[i].y};
solve();
for(int i=1;i<=n;i++)b[i]={a[i].x,-a[i].y};
solve();
printf("%lld\n",ans);
}
return 0;
}

D

不妨先随意找构造一个生成树