0%

AC代码与思路

相比于最后一次TLE背包提交,加了以下这些

1
for(int j=1;j<=x-sumYen;j++) {  // 背包容量为钱,背包价值为生产力
1
2
if(sumYen>x)
return false;

主要思路是二分答案加背包dp

背包容量为钱,背包价值为能加工多少产品

通过背包dp让加工产品最大化

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e9)+7,MAXA=1e7+7;
int n,x,mach[MAXN][6];
int dp[MAXA];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
dp[0]=0;
int cap1=mach[i][1],p1=mach[i][2],cap2=mach[i][3],p2=mach[i][4];
bool isBreak=false;
if(sumYen>x)
return false;
for(int j=1;j<=x-sumYen;j++) { // 背包容量为钱,背包价值为能加工多少产品
int up1=j-p1,up2=j-p2;
if(up1>=0)
dp[j]=dp[up1]+cap1;
if(up2>=0)
dp[j]=max(dp[j],dp[up2]+cap2);
if(dp[j]>=needCap) {
sumYen+=j;isBreak=true;break;
}
}
if(!isBreak)
return false;
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
// AC 64 个点 https://atcoder.jp/contests/abc374/submissions/58537183 已经比较接近了
// 就TLE了3个点 https://atcoder.jp/contests/abc374/submissions/58545771
// AC https://atcoder.jp/contests/abc374/submissions/58545889
// AC https://www.luogu.com.cn/record/180827335

以下是错误提交记录与心路历程

这个程序实际上没啥太大问题,但是即便开long long minYen还是超范围溢出了。

这个题目再次证明了,二分的题目r不能设太大,一个是复杂度,还有一个是溢出。

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e13)+7;
ll n,x,mach[MAXN][6];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
if(sumYen>x)
return false;
ll cap1=mach[i][1],p1=mach[i][2],cap2=mach[i][3],p2=mach[i][4];
ll minYen=0;ll needMach1=(needCap-1)/cap1+1,needMach2=(needCap-1)/cap2+1; // (a-1)/b +1
minYen=needMach1*p1; // 向上取整的trick
minYen=min(minYen,needMach2*p2);
sumYen+=minYen;
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
// AC 34 WA 64 https://atcoder.jp/contests/abc374/submissions/58535735

原来两台机子都能用!那check()函数写起来是比较麻烦

1
2
Both machines S_i and T_i can be used for process i.
If you purchase both machines S_i and T_i, the number of items that can be processed in process i will be the sum of the items processed by both machines.

AC 64 个点 https://atcoder.jp/contests/abc374/submissions/58537475

加上了对于组合的判定

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e10)+7;
ll n,x,mach[MAXN][6];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
if(sumYen>x)
return false;
ll cap1=mach[i][1],p1=mach[i][2],cap2=mach[i][3],p2=mach[i][4];
ll minYen;ll needMach1=(needCap-1)/cap1+1,needMach2=(needCap-1)/cap2+1; // (a-1)/b +1
minYen=min(needMach1*p1,needMach2*p2);
ll firstMach1=needCap/cap1,remainMach2=(needCap-firstMach1*cap1-1)/cap2+1;
ll priceComb1=firstMach1*p1+remainMach2*p2;
ll firstMach2=needCap/cap2,remainMach1=(needCap-firstMach2*cap2-1)/cap1+1;
ll priceComb2=firstMach2*p2+remainMach1*p1;;
minYen=min(minYen,min(priceComb1,priceComb2));
sumYen+=minYen;
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
// AC 64 个点 https://atcoder.jp/contests/abc374/submissions/58537183 已经比较接近了

我怀疑是我的向上取整模版的问题

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <cmath>
using namespace std;
long long a=0;
int main()
{
cout<<-1/782<<endl;
std::cout << (0-1)/782+1 << std::endl; // 模版行
cout<<ceil(a/1.0/782)<<endl;
return 0;
}
1
2
3
0
1
0

毫无疑问,0/782=0,你就算加ceil()也应该是0,但显然我们的模版出了问题。

哎,用系统ceil()提交了一遍,发现又WA了,和前面一模一样,只能说就这样吧,累了。

https://atcoder.jp/contests/abc374/submissions/58539387

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e10)+7;
ll n,x,mach[MAXN][6];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
if(sumYen>x)
return false;
ll cap1=mach[i][1],p1=mach[i][2],cap2=mach[i][3],p2=mach[i][4];
ll minYen;ll needMach1=ceil(needCap/1.0/cap1),needMach2=ceil(needCap/1.0/cap2); // (a-1)/b +1
minYen=min(needMach1*p1,needMach2*p2);
ll firstMach1=needCap/cap1,remainMach2=ceil((needCap-firstMach1*cap1)/1.0/cap2);
ll priceComb1=firstMach1*p1+remainMach2*p2;
ll firstMach2=needCap/cap2,remainMach1=ceil((needCap-firstMach2*cap2)/1.0/cap1);
ll priceComb2=firstMach2*p2+remainMach1*p1;;
minYen=min(minYen,min(priceComb1,priceComb2));
sumYen+=minYen;
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
// AC 64 个点 https://atcoder.jp/contests/abc374/submissions/58537183 已经比较接近了

引入了性价比概念,但发现连样例都过不了😂,还是要用dp。

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e9)+7,MAXA=1e7+7;
ll n,x,mach[MAXN][6];
int dp[MAXA];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
if(sumYen>x)
return false;
ll sumCap=0,capA=mach[i][1],pA=mach[i][2],capB=mach[i][3],pB=mach[i][4];
while (true) {
if(sumCap>=needCap)
break;
ll remainCap=needCap-sumCap;
double effA=pA*1.0/min(remainCap,capA); // 采用A机器的零件均摊价格(总消耗钱数/每台机器能做的有贡献零件数)
double effB=pB*1.0/min(remainCap,capB); // 采用B机器零件均摊价格
if(effA<effB)
sumCap+=capA,sumYen+=pA;
else
sumCap+=capB,sumYen+=pB;
}
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
// for(int i=1;i<=n;i++) {
// if(mach[i][2]/1.0/mach[i][1]>mach[i][4]/1.0/mach[i][3]) {
// swap(mach[i][1],mach[i][3]);
// swap(mach[i][2],mach[i][4]);
// }
// }
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}

将中间的判定改为了背包dp,果然这种通用做法就是应用性广泛且经过证明
// 就TLE了3个点 https://atcoder.jp/contests/abc374/submissions/58545771

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
// https://atcoder.jp/contests/abc374/tasks/abc374_e
#include <iostream>
#include <cmath>
typedef long long ll;
using namespace std;
const ll MAXN=105,MAXANS=static_cast<ll>(1e9)+7,MAXA=1e7+7;
int n,x,mach[MAXN][6];
int dp[MAXA];
inline bool check(ll needCap) {
ll sumYen=0;
for(int i=1;i<=n;i++) {
dp[0]=0;
int cap1=mach[i][1],p1=mach[i][2],cap2=mach[i][3],p2=mach[i][4];
bool isBreak=false;
if(sumYen>x)
return false;
for(int j=1;j<=x-sumYen;j++) { // 背包容量为钱,背包价值为生产力
dp[j]=0;
int up1=j-p1,up2=j-p2;
if(up1>=0)
dp[j]=dp[up1]+cap1;
if(up2>=0)
dp[j]=max(dp[j],dp[up2]+cap2);
if(dp[j]>=needCap) {
sumYen+=j;isBreak=true;break;
}
}
if(!isBreak)
return false;
}
if(sumYen<=x)
return true;
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>x;
for(int i=1;i<=n;i++) {
// cap1 price1 per U
cin>>mach[i][1]>>mach[i][2]>>mach[i][3]>>mach[i][4];
}
// for(int i=1;i<=n;i++) {
// if(mach[i][2]/1.0/mach[i][1]>mach[i][4]/1.0/mach[i][3]) {
// swap(mach[i][1],mach[i][3]);
// swap(mach[i][2],mach[i][4]);
// }
// }
ll l=0,r=MAXANS;
while(l<r) {
ll mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<l<<endl;
return 0;
}
// AC 64 个点 https://atcoder.jp/contests/abc374/submissions/58537183 已经比较接近了
// 就TLE了3个点 https://atcoder.jp/contests/abc374/submissions/58545771

当年用python写的代码

193.18pts TLE 3/22 WA 2/22

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=70565258

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
from collections import deque

# 输入
y_, x_ = map(int, input().split())
maze = [list(input()) for _ in range(y_)]

# 各个方向都能走 不能走上 不能走下 不能走左 不能走右
qList = [(0, 0, 0, 4, 1), (0, 0, 0, 3, 1), (0, 0, 0, 2, 1), (0, 0, 0, 1, 1), (0, 0, 0, 0, 1)] # x, y, d, 健全, 穿墙次数
direction5 = [[(-1, 0), (0, 1), (0, -1)], [(1, 0), (0, 1), (0, -1)], [(1, 0), (-1, 0), (0, 1)],
[(1, 0), (-1, 0), (0, -1)], [(1, 0), (-1, 0), (0, 1), (0, -1)]]

def bfs():
visited = [[[False] * 2 for _ in range(x_)] for _ in range(y_)]
q = deque(qList)
while q:
x0, y0, d, dI, throughAb = q.popleft()
if visited[y0][x0][throughAb]:
continue
visited[y0][x0][throughAb] = True

for dx, dy in direction5[dI]:
nx, ny = x0 + dx, y0 + dy
if 0 <= nx < x_ and 0 <= ny < y_:
# 终点判断
if nx == x_ - 1 and ny == y_ - 1:
if maze[ny][nx] == '.' or (maze[ny][nx] == 'X' and throughAb == 1):
return d + 1

# 走路与穿墙判断
if maze[ny][nx] == '.' and not visited[ny][nx][throughAb]:
q.append((nx, ny, d + 1, dI, throughAb))
elif maze[ny][nx] == 'X' and throughAb == 1 and not visited[ny][nx][0]:
q.append((nx, ny, d + 1, dI, 0))

return -1

print(bfs())

60pts TLE 可能是check函数复杂度太高了

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
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10,maxans=1e13+7;
ll n,m,a[maxn];
inline bool check(ll x) {
ll s=1,subCnt=0;
while (s<=n) {
ll res=0,cnt=0;
for(ll i=s;i<=n;i++) {
if(res+a[i]>x)
break;
res+=a[i];
cnt+=1;
}
s+=cnt;
subCnt+=1;
}
if(subCnt<=m) {
return true;
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
ll l=1,r=maxans;
while(l<r) {
ll mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
// 60pts https://www.luogu.com.cn/record/180491211

可以二分里套二分,当然那个二分我们就不写了,直接上stl

P1182_2.in

P1182_2.out

不过我仔细分析了一下时间复杂度,以及这个样例(另一道题目https://www.luogu.com.cn/problem/P2884)

1
2
3
4
5
6
7
8
7 5
100
400
300
100
500
101
400
1
500

会使上面这个程序死循环,让我确信不是check()的问题

仔细分析过后,发现还是check()的问题,check死循环了。

1
2
3
4
5
6
7
8
9
10
11
while (s<=n) {
ll res=0,cnt=0;
for(ll i=s;i<=n;i++) {
if(res+a[i]>x) // 某些情况下,cnt走不到cnt+1,导致s停滞不前,导致死循环
break;
res+=a[i];
cnt+=1;
}
s+=cnt;
subCnt+=1;
}

解决起来倒也简单

ll l=maxa,r=maxans;

让左端点比数组中最大的元素大,就可以了,这样可以保证cnt的前进。

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
// https://www.luogu.com.cn/problem/P2884
// https://www.luogu.com.cn/problem/P1182 双倍经验
#include <iostream>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10,maxans=1e13+7;
ll n,m,a[maxn];
inline bool check(ll x) {
ll s=1,subCnt=0;
while (s<=n) {
ll res=0,cnt=0;
for(ll i=s;i<=n;i++) {
if(res+a[i]>x)
break;
res+=a[i];
cnt+=1;
}
s+=cnt;
subCnt+=1;
}
if(subCnt<=m) {
return true;
}
return false;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>m;
ll maxa=0;
for(int i=1;i<=n;i++) {
cin>>a[i],maxa=max(maxa,a[i]);
}
ll l=maxa,r=maxans;
while(l<r) {
ll mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
return 0;
}
// 60pts TLE https://www.luogu.com.cn/record/180491211
// 40pts TLE https://www.luogu.com.cn/record/180498527
// AC https://www.luogu.com.cn/record/180509878
// AC https://www.luogu.com.cn/record/180510298

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
// https://www.acwing.com/problem/content/791/
#include <iostream>
using namespace std;
const int maxn=100007;
int n,q,k,a[maxn];

int main()
{
cin>>n>>q;
for(int i=1;i<=n;i++) {
cin>>a[i];
}
for(int _=1;_<=q;_++) {
cin>>k;
int l=1,r=n;
while (l<r) {
int mid=l+r>>1;
if(a[mid]>=k) r=mid;
else l=mid+1;
}
if(a[l]!=k) {
cout<<"-1 -1"<<endl;
continue;
}
int l2=1,r2=n;
while (l2<r2) {
int mid=l2+r2+1>>1;
if(a[mid]<=k) l2=mid;
else r2=mid-1;
}
cout<<l-1<<" "<<l2-1<<endl;
}
return 0;
}
// AC https://www.acwing.com/problem/content/submission/code_detail/37312161/


题目大意

给定 n 条线段,每条线段有两个端点坐标。移动时分为两种速度:移动到某条线段的起点/终点使用速度 s,沿着线段绘制使用速度 t。需要选择绘制线段的顺序及每条线段从哪一端开始,求完成全部绘制的最短总时间。

Pass 10/74 WA https://atcoder.jp/contests/abc374/submissions/58484468 赛时代码

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
#include <iostream>
#include <bitset>
#include <vector>
#include <algorithm>
#include <cmath>
#include <set>
#include <deque>
using namespace std;
int n,s,t,pos[10][6]; // s->not emitting laser && t-> emitting ......
bool vis[10][6],vis2[10];
double ans=10000000,dis;
void dfs(int x,int y,int cnt){
if(cnt>=n){
ans=min(dis,ans);
return;
}
for(int i=1;i<=n;i++){
if(vis2[i]) continue;
vis2[i]=true;
double squareS1=(pos[i][3]-pos[i][1])*(pos[i][3]-pos[i][1]);
double squareS2=(pos[i][4]-pos[i][2])*(pos[i][4]-pos[i][2]);
double lenSeg=sqrt(squareS1+squareS2)/t;
for(int j=1;j<=3;j+=2){
double square1=(pos[i][j]-pos[x][y])*(pos[i][j]-pos[x][y]);
double square2=(pos[i][j+1]-pos[x][y+1])*(pos[i][j+1]-pos[x][y+1]);
double ldis=sqrt(square1+square2)/s;
dis=dis+(ldis+lenSeg);
dfs(i,j,cnt+1);
dis=dis-(ldis+lenSeg);
}
vis2[i]=false;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>s>>t;
for(int i=1;i<=n;i++){
cin>>pos[i][1]>>pos[i][2]>>pos[i][3]>>pos[i][4];
}
dis=0;
dfs(0,0,0);
cout<<ans<<endl;
//cin>>n;
}
// WA https://atcoder.jp/contests/abc374/submissions/58484468

只改了一个地方就AC了
dfs(i,4-j,cnt+1); // 进入点和终止点不是一个,这条线段的终止点就是下一条线段的起始点

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
#include <iostream>
#include <bitset>
#include <vector>
#include <algorithm>
#include <cmath>
#include <set>
#include <deque>
using namespace std;
int n,s,t,pos[10][6]; // s->not emitting laser && t-> emitting ......
bool vis[10][6],vis2[10];
double ans=10000000,dis;
void dfs(int x,int y,int cnt){
if(cnt>=n){
ans=min(dis,ans);
return;
}
for(int i=1;i<=n;i++){
if(vis2[i]) continue;
vis2[i]=true;
double squareS1=(pos[i][3]-pos[i][1])*(pos[i][3]-pos[i][1]);
double squareS2=(pos[i][4]-pos[i][2])*(pos[i][4]-pos[i][2]);
double lenSeg=sqrt(squareS1+squareS2)/t;
for(int j=1;j<=3;j+=2){
double square1=(pos[i][j]-pos[x][y])*(pos[i][j]-pos[x][y]);
double square2=(pos[i][j+1]-pos[x][y+1])*(pos[i][j+1]-pos[x][y+1]);
double ldis=sqrt(square1+square2)/s;
dis=dis+(ldis+lenSeg);
dfs(i,4-j,cnt+1); // 进入点和终止点不是一个,这条线段的终止点就是下一条线段的起始点
dis=dis-(ldis+lenSeg);
}
vis2[i]=false;
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>s>>t;
for(int i=1;i<=n;i++){
cin>>pos[i][1]>>pos[i][2]>>pos[i][3]>>pos[i][4];
}
dis=0;
dfs(0,0,0);
cout.precision(17); // 打印double全精度
cout<<ans<<endl;
//cin>>n;
}
//AC https://atcoder.jp/contests/abc374/submissions/58506793
//AC https://www.luogu.com.cn/record/180435741