0%

2022杭电杯超级联赛06

1006-Meax

来源: 杭电杯超级联赛6 算法: DFS, 贪心 题目链接: https://acm.hdu.edu.cn/contest/problem?cid=1049&pid=1006 补完: No ## 代码

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
#include<iostream>
#include<vector>
using namespace std;
long long a[500010];
long long b[500010];
vector<int> v[500010];
void dfs(int x,int r){
a[x]=1;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==r) continue;
dfs(y,x);
a[x]+=a[y];
}
//
//cout<<a[x]<<endl;
//
return ;
}
void toans(int x,int r){
b[x]=a[x];
long long maxx=0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==r) continue;
toans(y,x);
maxx=max(maxx,b[y]);
}
b[x]+=maxx;
return;
}
signed main(){
ios::sync_with_stdio(false);
std::cin.tie(0);
int t;
cin>>t;
while(t--){
int n;
cin>>n;
for(int i=1;i<=n;i++){
v[i].clear();
}
for(int i=1;i<=n;i++){
a[i]=0;
b[i]=0;
}
for(int i=1;i<=n-1;i++){
int u1,u2;
cin>>u1>>u2;
v[u1].push_back(u2);
v[u2].push_back(u1);
}
dfs(1,0);
toans(1,0);
cout<<b[1]<<"\n";
}
}
# 1009-Map

来源: 杭电杯超级联赛6 算法: 向量, 计算几何 题目链接: https://acm.hdu.edu.cn/contest/problem?cid=1049&pid=1009 补完: Yes 完成时间: August 12, 2022

题解

有大地图\(M\)经过旋转压缩得到\(m\), 任落在\(M\)内, 在空间中有一不动点同时在\(M\)\(m\)上确定同一位置.

设不动点为\(P\), 设相对位置参数\(\lambda,\mu\), 可列方程

\[ \begin{align} \vec{OP}&=\vec{OA}+\lambda\vec{AB}+\mu\vec{AD}\\ \vec{OP}&=\vec{Oa}+\lambda\vec{ab}+\mu\vec{ad} \end{align} \]

化简有

\[ \begin{align} \vec{AP}&=\lambda\vec{AB}+\mu\vec{AD}\\ \vec{aP}&=\lambda\vec{ab}+\mu\vec{ad} \end{align} \]

展开有

\[ \begin{align} P.x-A.x&=\lambda(B.x-A.x)+\mu(D.x-A.x)\\ P.y-A.y&=\lambda(B.y-A.y)+\mu(D.y-A.y)\\ P.x-a.x&=\lambda(b.x-a.x)+\mu(d.x-a.x)\\ P.y-a.y&=\lambda(b.y-a.y)+\mu(d.y-a.y)\\ \end{align} \]

化简有

\[ \begin{align} a.x-A.x&=\lambda(B.x-b.x+a.x-A.x)+\mu(D.x-d.x+a.x-A.x)\\ a.y-A.y&=\lambda(B.y-b.y+a.y-A.y)+\mu(D.y-d.y+a.y-A.y)\\ \end{align} \]

展开有

\[ \begin{align} (a.x-A.x)(D.y-d.y+a.y-A.y)&=\lambda(B.x-b.x+a.x-A.x)(D.y-d.y+a.y-A.y)+\mu(D.x-d.x+a.x-A.x)(D.y-d.y+a.y-A.y)\\ (a.y-A.y)(D.x-d.x+a.x-A.x)&=\lambda(B.y-b.y+a.y-A.y)(D.x-d.x+a.x-A.x)+\mu(D.x-d.x+a.x-A.x)(D.y-d.y+a.y-A.y)\\ \end{align} \]

\[ \begin{align} (a.x-A.x)(B.y-b.y+a.y-A.y)&=\lambda(B.x-b.x+a.x-A.x)(B.y-b.y+a.y-A.y)+\mu(D.x-d.x+a.x-A.x)(B.y-b.y+a.y-A.y)\\ (a.y-A.y)(B.x-b.x+a.x-A.x)&=\lambda(B.x-b.x+a.x-A.x)(B.y-b.y+a.y-A.y)+\mu(D.y-d.y+a.y-A.y)(B.x-b.x+a.x-A.x)\\ \end{align} \]

化简有

\[ \lambda=\frac{(a.x-A.x)(D.y-d.y+a.y-A.y)-(a.y-A.y)(D.x-d.x+a.x-A.x)}{(B.x-b.x+a.x-A.x)(D.y-d.y+a.y-A.y)-(B.y-b.y+a.y-A.y)(D.x-d.x+a.x-A.x)} \]

\[ \mu=\frac{(a.x-A.x)(B.y-b.y+a.y-A.y)-(a.y-A.y)(B.x-b.x+a.x-A.x)}{(D.x-d.x+a.x-A.x)(B.y-b.y+a.y-A.y)-(D.y-d.y+a.y-A.y)(B.x-b.x+a.x-A.x)} \]

最后有

\[ \begin{align} P.x&=\lambda(B.x-A.x)+\mu(D.x-A.x)+A.x\\ P.y&=\lambda(B.y-A.y)+\mu(D.y-A.y)+A.y\\ \end{align} \]

代码

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
#include<bits/stdc++.h>
using namespace std;
void solve(){
double Ax,Ay,Bx,By,Cx,Cy,Dx,Dy;
double ax,ay,bx,by,cx,cy,dx,dy;
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&Ax,&Ay,&Bx,&By,&Cx,&Cy,&Dx,&Dy);
scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy);
double alphaX = ax-Ax;
double alphaY = ay-Ay;
double betaX = Bx-bx+alphaX;
double betaY = By-by+alphaY;
double deltaX = Dx-dx+alphaX;
double deltaY = Dy-dy+alphaY;
double lambda = (alphaX*deltaY-alphaY*deltaX)/( betaX*deltaY- betaY*deltaX);
double mu = (alphaX* betaY-alphaY* betaX)/(deltaX* betaY-deltaY* betaX);
double Px = lambda*(Bx-Ax)+mu*(Dx-Ax)+Ax;
double Py = lambda*(By-Ay)+mu*(Dy-Ay)+Ay;
printf("%.15lf %.15lf\n",Px,Py);
}
int main(){
int T;
cin>>T;
while(T--)solve();
return 0;
}

1012-Loop

来源: 杭电杯超级联赛6 算法: 字典序, 栈 题目链接: https://acm.hdu.edu.cn/contest/problem?cid=1049&pid=1012 补完: Yes 完成时间: August 16, 2022

题意简述

给定一个长度为n的数组a,进行严格的k次操作:选择两个整数l,r (1≤l≤r≤n),让数组a_l到a_r循环左移一位。

使得数组a的字典序最大,输出操作后的数组a。其中字典序比较的是每个a_i的值,不考虑a_i具体的数位。

题目分析

首先,由于l和r可以相等,所谓严格的k次操作其实相当于可以少于k次。

接着,一次操作之后,发生了什么?

不是循环左移一位吗题目又不是很难懂。

我们换个角度看看,既然操作的时候可以任选区间,并且循环左移意味着最左的数字换到区间最右端——那么一次操作实际上是否是任选一个作为区间左节点的数字,把它放到它之后的任意位置呢?是的。

也就是作为左区间的每个数字,都可以自由地插入其后的位置。

既然是自由插入,那我们当然是直接把这部分自由的数字排序好放进去啦~

那么怎么放进去呢?

我们考虑插入前的序列的有序情况:

如果原来的序列是降序的,自由插入一些数字,字典序最小的结果其实就是所有数字降序排列的结果。

如果原来的序列是无序的,对于已经预先排好的待插入数字,每次都插在第一个比自己小的数字之前,才能让字典序更大。

并且,使用无序的一般情况的方案插入有序数列,也能得到有序数列的最优方案,故而我们采用这种混合插入的方式来插入数列。

那区分这个干什么啊。

对待插入的原序列的分类得出的结论,很好地辅助了我们思考如何选取这些自由插入的数字。

首先的首先,由于字典序先比较数列前端,那么在前面的数字优先。

接着,回看前面插入有序序列的情况:往降序序列插入自由的元素,能得到所有元素降序排列的结果。这意味着没有必要把降序的序列数纳入自由元素,也能达到所有元素降序的最优的字典序结果。并且为了节省更多机会,相等的连续元素也视为降序,也就是非严格降序序列都不用纳入自由元素。

而这样节省下来的操作次数,可以抓取更多的后面无序序列的元素,让它变成自由排列的。

换句话说,我们选取破坏了非严格降序序列的数字。

再然后,怎·么判断不用抓取的非严格降序区问呢?

想象两个相邻的非严格降序区问,后一个区问最前端的一小段比前—个区间最后端的一段元素大。如果直接在原序列里做判断,不管怎样都是不够正确的(这里认为k足够大):

后—个区间的第─个数字大于前—个区间的最后—个数字了,认为非严格降序区间结束了,把后—个区问连续扔进自由元素里,显然浪费了操作机会——删去前面—小段就能让剩下的数字合成—个非严格降序区间了。 认为两个区问都是非严格降序区间,在第二个区问之后去抓自由元素,显然结果不够优秀:在更前面的,第三个区间的前端元素偏大,在字典序比较中,这已经比把这些元素抓走后的结果更小了。

那反正就是把中间的抓出来嘛!

然而把第二个区间前一段偏大的抓走也是错误的。

别忘了,”自由“的元素也不是完全自由的。

它们只能往自己的后方插入。看图来理解这个选择:

把两个区间描述成这样的曲线,两个区间有一段大小叠合:

Untitled

如果是把后一个区间的bc段纳入自由元素,那么它在自由排序的时候会插入ab段中。这是不符合规则的。只有把前一个区间的ab段扔到自由元素里才行——它们会排列进bc段,这是合法的操作。

Untitled

那么怎么把这些元素放入自由序列呢?双指针指向需要比较的元素也是可行的,但出于好写和好懂的考虑,这里使用了栈的结构。

从原始序列的队首开始,依次把元素压入栈中。

如果新入栈元素比栈顶元素小或相等,不严格降序的结构不被破坏,继续入栈即可。

如果新入栈元素比栈顶元素大,不严格降序的结构被破坏,把原来的栈顶元素弹出,进入自由元素的队列中,消耗一次操作机会(也就是入自由元素队的机会)。继续把新元素和新的栈顶比较,直到不严格降序的结构不被破坏。

最后,我们得到了长度为k的自由元素队,把它们排序后插入序列中即可。

注意,仍然留在栈中的元素记得退回序列。

完整代码

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
#include<iostream>
#include<algorithm>
using namespace std;
int a[300010];
int b[300010];//自由排序队
int c[300010];//比较等待栈
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int T;
cin>>T;
while(T--){
int n,k;
cin>>n>>k;
for(int i=0;i<n;i++)cin>>a[i];
int stpb=0,stpc=0,stpa;
for(int i=0;i<n&&k;i++){
if(stpc==0){
c[stpc++]=a[i];//空栈则直接入栈
stpa=i;
}
else{
if(c[stpc-1]<a[i]){
while(true){
if(c[stpc-1]>=a[i])break;
b[stpb++]=c[stpc-1];//栈顶进队
stpc--;
k--;//消耗一次进队机会
if(stpc==0||k==0)break;
}
if(k){
c[stpc++]=a[i];
stpa=i;
}
}
else{
c[stpc++]=a[i];//新数进栈
stpa=i;
}
}
}
for(int i=stpc-1;i>=0;i--)a[stpa--]=c[i];//栈退回原始序列
stpa++;
sort(b,b+stpb);
stpb--;
int flag=0;
while(true){
if(b[stpb]>a[stpa]||stpa>=n){
if(flag==0){
cout<<b[stpb--];
flag=1;
}
else
cout<<' '<<b[stpb--];
}
else {
if(flag==0){
cout<<a[stpa++];
flag=1;
}
else
cout<<' '<<a[stpa++];
}
if(stpb<0)break;
}

for(int i=stpa;i<n;i++){
if(flag==0){
cout<<a[i];
}
cout<<' '<<a[i];
}
cout<<'\n';
}
}