【SinGuLaRiTy-1045】 Copyright (c) SinGuLaRiTy 2017. All Rights Reserved.
| 源文件名 | 输入输出文件 | 时间限制 | 内存限制 |
淘气的cch | cch.cpp | cch.in/out | 1500ms | 600MB |
高爆螺旋狗 | hedog.cpp | hedog.in/out | 1000ms | 512MB |
毁灭计划 | destroy.cpp | destroy.in/out | 3000ms | 256MB |
淘气的cch
题目描述
cch太淘气了,一天,SunIsMe发现他把yhn家的墙壁都重新粉刷了一遍,yhn家的每块墙壁可以被分成M*N块,每一块有一种颜色(可以用一个整数表示),cch每粉刷一面墙壁有且仅把某一矩形区域刷成自己想要的颜色。yhn回家发现cch在搞事情,把cch打了一顿解气后,发现cch很有艺术天赋啊,每一面墙都是一幅抽象派巨著啊!不过,yhn可不想让他的每一块墙壁长得不一样,于是他让cch把他涂过的墙壁重新粉刷成一样的。粉刷墙壁是要耗体力的,每次cch可以改变一块墙壁中某一块的颜色,让其加1或减1,cch还想至少让自己的一幅画保留下来(也就是说至少有一块墙壁不重新粉刷),不过cch觉得自己笨成yhn了,于是拜托可以轻松AK这套题的你来帮忙。
一句话题意:有若干个整数矩阵,要求选择一个矩阵,使得它与其他矩阵的距离之和最小,此处矩阵与矩阵的距离指对应位置上的整数之差的绝对值(至于矩阵怎么产生的请看样例解释)。
输入格式
第1行给出原来每块墙的长宽M,N,cch涂过的墙的数量K,和颜色的数量Q。
第2到M+1行,每行给出一个长度为N的整数串,表示原来每块墙壁的颜色。
第M+2到M+K+2行,每行五个整数ai,bi,ci,di,ei,表示cch涂过的第i块墙壁的(ai,bi)到(ci,di)这块矩形部分被ei覆盖。
输出格式
第1行,一个整数,表示最少的重新粉刷次数。
样例数据
样例输入1 | 样例输出1 |
3 3 2 3 | 10 |
样例输入2 | 样例输出2 |
5 5 3 10 | 59 |
<样例解释>
对于样例1,cch粉刷后产生了两个矩阵:
0 0 0 1 1 0 0 0 0
0 0 0 => 1 1 0 和 0 2 2
0 0 0 0 0 0 0 2 2
两个矩阵的距离就是(1-0)+(1-0)+(0-0)+(1-0)+(2-1)+(2-0)+(0-0)+(2-0)+(2-0)=10.
<数据范围>
对于所有数据,N,M<=1000, K<=300000, Q<=30。
解析
维护每一种颜色在每一面墙中出现次数和的二维前缀和,枚举选择的墙,记每一面墙与原墙的距离为Total,则Ans=Total-被修改的矩形中每一面墙与原墙的距离+被修改的矩形中其它的墙与被选择墙的距离,更新答案即可。
时间复杂度 O(MNQ+QK)
Code
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #include<cmath> #include<time.h> using namespace std; #define MAXN 1000 #define MAXM 1000 #define MAXK 300000 #define MAXQ 30 #define INF 0x3f3f3f3f #define LLF 0x3f3f3f3f3f3f3f3fll typedef long long int LL; void Read(int &x){ x=0;char c=getchar();bool flag=0; while(c<'0'||'9'<c){if(c=='-')flag=1;c=getchar();} while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();} if(flag)x=-x; } int M,N,K,Q; int A[MAXK+10],B[MAXK+10],C[MAXK+10],D[MAXK+10],E[MAXK+10]; LL num[MAXM+10][MAXN+10][MAXQ+5];//第k种字符的数量 LL val[MAXM+10][MAXN+10][MAXQ+5];//第k重字符的距离贡献 LL cnt[MAXM+10][MAXN+10];//(i,j)上的修改次数 int str[MAXM+10][MAXN+10]; LL Total;//每一种版本与原图的距离之和 LL query(int a,int b,int c,int d,int e){ LL rn=Total;//假设一开始是初始矩形 for(int i=0;i<Q;++i){ //加上当前版本覆盖矩形中和其它版本的距离 rn+=abs(i-e)*(num[c][d][i]-num[a-1][d][i]-num[c][b-1][i]+num[a-1][b-1][i]); //减去当前版本覆盖矩形中原来的影响 rn-=val[c][d][i]-val[a-1][d][i]-val[c][b-1][i]+val[a-1][b-1][i]; } return rn; } int main(){ freopen("cch.in","r",stdin); freopen("cch.out","w",stdout); Read(M),Read(N),Read(K),Read(Q); for(int i=1;i<=M;++i) for(int j=1;j<=N;++j)Read(str[i][j]); for(int i=1;i<=K;++i){ Read(A[i]),Read(B[i]),Read(C[i]),Read(D[i]),Read(E[i]); //差分 ++num[A[i]][B[i]][E[i]],++num[C[i]+1][D[i]+1][E[i]]; --num[C[i]+1][B[i]][E[i]],--num[A[i]][D[i]+1][E[i]]; } //统计出每一个位置的字符数量 for(int i=1;i<=M;++i) for(int j=1;j<=N;++j) for(int k=0;k<Q;++k){ num[i][j][k]+=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]; cnt[i][j]+=num[i][j][k]; } for(int i=1;i<=M;++i) for(int j=1;j<=N;++j) num[i][j][str[i][j]]+=K-cnt[i][j]; //计算贡献 Total=0; for(int i=1;i<=M;++i) for(int j=1;j<=N;++j) for(int k=0;k<Q;++k){ val[i][j][k]=abs(k-str[i][j])*num[i][j][k]; Total+=val[i][j][k]; } //累加矩形前缀和 for(int i=1;i<=M;++i) for(int j=1;j<=N;++j) for(int k=0;k<Q;++k){ num[i][j][k]+=num[i-1][j][k]+num[i][j-1][k]-num[i-1][j-1][k]; val[i][j][k]+=val[i-1][j][k]+val[i][j-1][k]-val[i-1][j-1][k]; } LL ans=LLF; for(int i=1;i<=K;++i) ans=min(ans,query(A[i],B[i],C[i],D[i],E[i])); printf("%I64d\n",ans); //printf("%0.6lf\n",(double)clock()/CLOCKS_PER_SEC); }
高爆螺旋狗
题目描述
社会主义阵营为了防御资本主义阵营的攻击,驯养了一只高爆螺旋狗,这只狗吃的火药越多,爆炸半径越大。如果资本主义的毒瘤 cch 带领他的克隆体进犯,会立马遭到螺旋狗的打击。
社会主义阵营的卫星会对战场拍摄二维图片 A。所谓‘图片’,是一个 N*N 的由小写字母组成的矩阵。Hineven 掌握 cch 的‘图片’是一个类似的由全小写字母组成,边长为 M 的二维矩阵 B,如果一个‘图片’R 中存在一种方案:挖出一块正方形区域使得其每个字母能与 B 中相同位置的字母一样,那么我们认为 B‘出现’在 R 中挖出的正方形区域内左上角一次。
cch 带着他的克隆体大军压境。Hineven 希望能用高爆螺旋狗一次消灭至少 K 只 cch 克隆体,高爆螺旋狗的爆炸范围是一个边长为 X 的正方形,并且可以直接作用在战场的任意地点,消灭‘出现’在作用范围内的所有 cch。但是每只 cch 克隆体的战斗力略有差别,位置为(i,j)的 cch 战斗力为整数 Cij,而高爆螺旋狗只能消灭战斗力在区间[L,R]内的 cch。L 和 R 满足等式 R-L=X-1。这意味着 Hineven 还要同时决定 L 与 R。
时间不多,Hineven 想让这只高爆螺旋狗至少炸飞 K 只 cch 克隆体,你需要尽快求出最小的 X,这直接决定了他会喂这只狗多少火药并且达到如何的杀伤效果。
一句话题意:给出两个全小写字母组成的正方形矩阵A和B和整数K,A与B的边长分别为 N 和 M,要求出最小的 X 使得有一种方案来从 A 中挖出一个 X*X 的‘子图片’让属性值在长度为 X 的区间内的 B 在‘子图片’中出现至少 K 次。
输入格式
第一行输入 N,M,K,让输入行数为 i 接下来 N 行,每行一个连续的小写长度为 N 的字符串表示矩阵 A 的第 i-1 行
接下来N 行,每行N 个用单个空格分开的整数表示能出现在i-(N+1)行的对应位置的cch克隆体的战斗力
接下来 M 行,每行一个连续的小写长度为 M 的字符串表示矩阵 B 的第 i-(N*2+1)行
输出格式
第一行输出一个整数,你的答案 X,如果你想告诉 Hineven 打败资本主义只是一个梦想(无解),输出-1。
样例数据
样例输入 | 样例输出 |
3 2 2 | 3 |
<样例解释>
圈出 A 中(1,1)到(3,3)的 3*3 的正方形矩阵可以达到两次‘出现’出现的两只 cch的战斗力分别为 0 和 1,选择[0,2]长度为 X 的区间可以全部包含在内。
<数据范围>
对于 10%的数据 N,M<=10
对于 30%的数据 N, M<=60 对于另 30%的数据 N<=600,M<=100
对于 100%的数据 1<=M<=N<=600, 0<=K<=10^7, -100<=Cij<=100,且数据较为随机化
解析
首先发现这道题肯定和字符串匹配有关,把 B 的每一行作为模式串和 A 的每一行进行匹配,用 KMP 跑 M*(N-M)次匹配能算出所有 B 在 A 中出现的位置
时间复杂度 O((N-M)*M*(N+M)),另外JeremyGuo提供了理论上更快的O(N^2) Hash算法,将在Code部分里额外附上。
然后发现 X 和出现数量成正比关系,考虑二分答案,把 A 中的横座标,纵座标,以及每只 cch 的‘战斗力’分别作为第一,第二,第三个维度进行三维前缀和,二分 X 后暴力枚举立方体左上角进行判定。时间复杂度 O(N^2*max{Cij}*logN)
总时间复杂度为 O((N-M)*M*(N+M) + N^2*max{Cij}*logN)
Code
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <cmath> typedef long long LL; const int MAXN = 710; const int MAXT = 201; using std :: min; using std :: max; char A[MAXN][MAXN], B[MAXN][MAXN]; int n, m, K; int nxt[MAXN]; void getNext (char * str, int len) { int j = 0, k = -1; nxt[0] = -1; while(j < len) if(k == -1 || str[j] == str[k]) nxt[++j] = ++k; else k = nxt[k]; } bool match[MAXN]; void doKMP (char * s1, char * s2, int l1, int l2) { memset(match, 0, sizeof match); if(l1 < l2) return ; int i, j; for(i = 0, j = 0; i<l1; i++, j++) { if(j == l2) {match[i-l2] = true; i--; j = nxt[j]-1;} else while(j != -1 && s1[i] != s2[j]) j = nxt[j]; } if(j == l2) match[l1 - l2] = true; } bool tmp[MAXN][MAXN]; int pref[MAXN][MAXN][210]; int attr[MAXN][MAXN]; bool check(int val) { int r = val; val -= m-1; for(int i = r; i<=n; i++) for(int j = r; j<=n; j++) for(int k = min(MAXT, r); k<=MAXT; k++) { int t = max(0, k-r); if(pref[i][j][k] - pref[i-val][j][k] - pref[i][j-val][k] - pref[i][j][t] + pref[i-val][j-val][k] + pref[i-val][j][t] + pref[i][j-val][t] - pref[i-val][j-val][t] >= K) return true; } return false; } inline int getInt() { int ret = 0; char ch; bool f = false; while((ch = getchar()) < '0' || ch > '9') f |= (ch == '-'); do {ret *= 10; ret += ch - '0';} while((ch = getchar()) >= '0' && ch <= '9'); return f ? -ret : ret; } int main () { freopen("hedog.in","r",stdin); freopen("hedog.out","w",stdout); scanf("%d%d%d", &n, &m, &K); if(K == 0) {puts("0");return 0;} for(int i = 1; i<=n; i++) scanf("%s", A[i]+1); for(int i = 1; i<=n; i++) for(int j = 1; j<=n; j++) attr[i][j] = getInt(); for(int i = 1; i<=m; i++) scanf("%s", B[i]+1); memset(tmp, 0xff, sizeof tmp); for(int j = 1; j<=m; j++) { getNext(B[j]+1, m); for(int i = j; i<=n-(m-j); i++) { doKMP(A[i]+1, B[j]+1, n, m); for(int k = 0; k<n; k++) tmp[i-j][k] &= match[k]; } } for(int i = m; i<=n; i++) for(int j = m; j<=n; j++) { if(tmp[i-m][j-m]) pref[i][j][attr[i-m+1][j-m+1]+101] = 1; for(int k = 1; k<=MAXT; k++) pref[i][j][k] += pref[i-1][j][k] + pref[i][j-1][k] + pref[i][j][k-1] - pref[i-1][j-1][k] - pref[i-1][j][k-1] - pref[i][j-1][k-1] + pref[i-1][j-1][k-1]; } int l = m, r = n, ans = n+1; while(l < r) { int mid = (l+r)>>1; if(check(mid)) r = mid, ans = min(ans, mid); else l = mid + 1; } if(ans == n+1) ans = -1; printf("%d\n", ans); }
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; const int MAXN = 600; const int MAXT = 201; const int MOD_ROW = 27; const int MOD_COL = 32777; const int MOD = 998244353; const int INF = 0x3f3f3f3f; char A[MAXN+10][MAXN+10], B[MAXN+10][MAXN+10]; int n, m, height[MAXN+10][MAXN+10]; pair<int,int> poss[MAXN*MAXN+10]; int sum[MAXN+10][MAXN+10][MAXT+2], K; namespace Hash{ int hs[MAXN+10][MAXN+10], row[MAXN+10][MAXN+10], pow_row[MAXN+10], pow_col[MAXN+10]; void Init(){ pow_col[0] = pow_row[0] = 1; for(int i=1;i<=n;i++){ pow_col[i] = 1ll * pow_col[i-1] * MOD_COL % MOD; pow_row[i] = 1ll * pow_row[i-1] * MOD_ROW % MOD; } for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) row[i][j] = (1ll*row[i][j-1]*MOD_ROW+A[i][j]-'a'+1)%MOD; for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) hs[i][j] = (1ll*hs[i-1][j]*MOD_COL+row[i][j])%MOD; } int GetHash(int x1, int y1, int x2, int y2){ int len_row = y2-y1+1; int len_col = x2-x1+1; int ret = ((hs[x2][y2]-1ll*hs[x1-1][y2]*pow_col[len_col]%MOD)%MOD+MOD)%MOD; int tmp = ((hs[x2][y1-1]-1ll*hs[x1-1][y1-1]*pow_col[len_col]%MOD)%MOD+MOD)%MOD; return ((ret-1ll*tmp*pow_row[len_row]%MOD)%MOD+MOD)%MOD; } int GetB(){ int ret = 0, tmp; for(int i=1;i<=m;i++){ tmp = 0; for(int j=1;j<=m;j++) tmp = (1ll*tmp*MOD_ROW+B[i][j]-'a'+1)%MOD; ret = (1ll*ret*MOD_COL+tmp)%MOD; } return ret; } } template<class T> void Read(T& x){ char ch; int f = 1; while(ch=getchar(), ch<'0'||ch>'9') if(ch == '-') f = -f; x = ch-'0'; while(ch=getchar(), ch>='0'&&ch<='9') x = x*10+ch-'0'; ungetc(ch, stdin); x *= f; } bool check(int x1, int y1, int x2, int y2, int val){ //if(x2 <= 0 || y2 <= 0) return false; for(int i=1;i<=MAXT;i++){ int l=max(0, i-val); int sum1 = sum[x2][y2][i] - sum[x1-1][y2][i] - sum[x2][y1-1][i] + sum[x1-1][y1-1][i]; int sum2 = sum[x2][y2][l] - sum[x1-1][y2][l] - sum[x2][y1-1][l] + sum[x1-1][y1-1][l]; if(sum1 - sum2 >= K) return true; } return false; } int GetRand(int l, int r){ return rand()%(r-l+1)+l; } int main(){ srand(MOD); freopen("hedog.in", "r", stdin); freopen("hedog.out", "w", stdout); int vis; Read(n); Read(m); Read(K); if(!K){ printf("0\n"); return 0; } for(int i=1;i<=n;i++) scanf("%s", A[i]+1); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){ Read(height[i][j]); height[i][j] += 101; } for(int i=1;i<=m;i++) scanf("%s", B[i]+1); int hb = Hash::GetB(); Hash::Init(); for(int i=1;i<=n;i++){ if(i+m-1>n) break; for(int j=1;j<=n;j++){ if(j+m-1>n) break; vis = int(Hash::GetHash(i, j, i+m-1, j+m-1) == hb); //printf("1"); for(int k=1;k<=MAXT;k++){ sum[i][j][k] = sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k]; if(vis && height[i][j] <= k) sum[i][j][k]++; } } //puts(""); } int pcnt = 0; for(int i=1;i<=n;i++){ if(i+m-1 > n) break; for(int j=1;j<=n;j++){ if(j+m-1 > n) break; poss[++pcnt] = make_pair(i, j); } } for(int i=1;i<=pcnt;i++) swap(poss[i], poss[GetRand(1, i)]); int x=-1; for(int p=1;p<=pcnt;p++){ int i = poss[p].first, j = poss[p].second; int L=m, R=min(n-i+1, n-j+1); R = min(R, (x==-1?INF:x-1)); if(L <= R && !check(i, j, i+R-m, j+R-m, R)) continue; while(L <= R){ int mid = (L+R)>>1; if(check(i, j, i+mid-m, j+mid-m, mid)){ x = mid; R = mid-1; }else L = mid+1; } } printf("%d\n", x); return 0; }
毁灭计划
题目描述
资本主义cch在战场上建立了N个圆形堡垒,为此Ender作为一个熊孩子,想到了两个疯狂的毁灭计划,并将其命名为A计划和B计划。有了这两个计划,资本主义cch的阴谋就不会得逞。
A计划:压缩计划
压缩机产生的压强是非常恐怖的,于是乎Ender打算拆掉全世界所有的压缩机提炼出许多压缩核心并将其放置在圆形堡垒群外,形成一个封闭曲线,当所有的压缩核心开始运作,它们就会把堡垒压个粉碎。
B计划:铁板计划
这个计划非常的简单粗暴,Ender打算造一个巨型铁板,从天上扔下来直接拍死资本主义cch的堡垒。并且由于某种原因,任意两个被铁板覆盖的点之间的线段必须被铁板覆盖。
当0_1把这两个计划提交到总部后,总部觉得这两个计划非常的邪恶,决定两者都执行。
但计划的消耗是巨大的,并且要0_1自掏腰包,0_1打算尽可能减小这个消耗,A计划的消耗为封闭曲线的长度,B计划消耗为铁板面积。请你来计算最小消耗。
一句话题意:给出N个圆,和M,求一个圆的凸包,询问凸包周长和凸包面积(一看就是一道版题)
输入格式
第一行输入 N。
接下来 N 行,每行三个整数x,y,r。分别表示堡垒i的圆形坐标是(x,y),半径是r。
输出格式
输出两行,第一行表示计划A的消耗,第二行表示计划B的消耗。
保留三位小数。
样例数据
样例输入 | 样例输出 |
3 | 23.983 |
<样例解释>
如图
<数据范围>
对于 20%的数据 N<=10
对于 另30%的数据 N<=100
对于 另20%的数据 N<=500
对于 另30%的数据 N<=1000
对于 100%的数据 |x|,|y|<=10000,0<=r<=100,圆有可能退化为点,圆可能重叠。
解析
首先给出30分解法: 可以发现二进制枚举按得数量就行了,观察可以发现f(n)=phi(n) g(n)=n,呵呵。
根据30分的做法,我们可以发现phi(n)最多向下递归Log(n)层就变成了1,呵呵,然后我们可以观察发现每个门相当于一个条件,表示控制它的两个开关是否同时按下。然后利用并查集或者二分图染色,检查是否可行就行了。
Code
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> #include<cstdlib> #include<iostream> #include<ctime> #include<climits> #include<stack> #include<queue> #include<vector> #include<map> #include<set> typedef long long int LL; typedef unsigned long long ULL; const int INF=0x3f3f3f3f; LL getint(){ LL ans=0;int f=1;char c; while((c=getchar())<'0'||c>'9')if(c=='-')f=-f; while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();} return ans*f; } using namespace std; //from here const double eps=1e-10,PI=3.14159265358979323846264338328; bool equal(double a,double b){return a<=b+eps&&a>=b-eps;} int sign(double a){ if(a>eps)return 1; if(a<-eps)return -1; return 0; } bool big(double a,double b){return a>b+eps;} bool small(double a,double b){return a<b-eps;} struct point{ double x,y; point(){} int read(){return scanf("%lf%lf",&x,&y);} point(double _x,double _y){x=_x;y=_y;} point operator-()const{return point(-x,-y);} point operator+(point r)const{return point(x+r.x,y+r.y);} point operator-(point r)const{return point(x-r.x,y-r.y);} point operator*(double a)const{return point(x*a,y*a);} point operator/(double a)const{return point(x/a,y/a);} double operator*(point r)const{return x*r.x+y*r.y;} double operator^(point r)const{return x*r.y-y*r.x;} double length()const{return sqrt(x*x+y*y);} bool operator==(point r)const{return equal(x,r.x)&&equal(y,r.y);} point unit()const{return *this/length();} double atan(){return atan2(y,x);} }; struct Round{ point c;double r; void read(){c.read();scanf("%lf",&r);} point find(double a)const{return c+point(r*cos(a),r*sin(a));} vector<point> cutrd(const Round &B)const{ vector<point>ret; point C=B.c;double R=B.r; point bar=C-c; double d=bar.length(); if(small(d,abs(R-r))) return ret; else if(equal(d,abs(R-r))){ if(equal(R,r))return ret; point pos; if(big(R,r)) pos=find((-bar).atan()); else pos=find(bar.atan()); ret.push_back(pos); return ret; } double aph=asin((R-r)/d),bet=bar.atan(); aph+=PI/2; ret.push_back(find(bet+aph)); ret.push_back(find(bet-aph)); return ret; } bool in(const Round &B)const{ return !big(r,B.r)&&!big((c-B.c).length(),abs(B.r-r)); } }; struct data{ point c; int b; data(){} data(point _c,int _b){c=_c;b=_b;} bool operator<(const data &r)const{ return equal(c.x,r.c.x)?small(c.y,r.c.y):small(c.x,r.c.x); } }; //to here const int MAXN=1000; Round s[MAXN+10]; data p[2*MAXN*MAXN+10]; data ans[2*MAXN*MAXN+10]; bool used[2*MAXN+10]; int main(){ freopen("destroy.in","r",stdin); freopen("destroy.out","w",stdout); int n=getint(); for(int i=1;i<=n;++i) s[i].read(); int num=0,cnt=0,tmp=0,buf=0; for(int i=1;i<=n;++i)if(!used[i]) for(int j=1;j<=n;++j)if(i!=j&&!used[j]) if(s[j].in(s[i]))used[j]=1; for(int i=1;i<=n;++i) if(!used[i]){ ++tmp; buf=i; } if(tmp==1){ printf("%.3lf\n",2*PI*s[buf].r); printf("%.3lf\n",PI*s[buf].r*s[buf].r); return 0; } for(int i=1;i<=n;++i)if(!used[i]) for(int j=1;j<=n;++j)if(!used[j]){ vector<point>ret=s[i].cutrd(s[j]); for(int t=0;t<ret.size();++t) p[++num]=data(ret[t],i); } sort(p+1,p+num+1); tmp=0; for(int i=1;i<=num;++i){ while(i<num&&p[i].c==p[i+1].c) ++i; p[++tmp]=p[i]; } num=tmp; ans[++cnt]=p[1]; ans[++cnt]=p[2]; for(int i=3;i<=num;++i){ while(cnt>1&&sign((p[i].c-ans[cnt].c)^(ans[cnt].c-ans[cnt-1].c))>=0) --cnt; ans[++cnt]=p[i]; } tmp=cnt; ans[++cnt]=p[num-1]; for(int i=num-2;i>=1;--i){ while(cnt>tmp&&sign((p[i].c-ans[cnt].c)^(ans[cnt].c-ans[cnt-1].c))>=0) --cnt; ans[++cnt]=p[i]; } ans[0]=ans[cnt-1]; ans[cnt+1]=ans[2]; double op=0,ed=0; for(int i=1;i<cnt;++i){ ed+=ans[i].c^ans[i+1].c; if(ans[i].b==ans[i+1].b){ int r=s[ans[i].b].r; double aph=(ans[i].c-ans[i-1].c).atan(); double bet=(ans[i+2].c-ans[i+1].c).atan(); bet-=aph; if(big(bet,2*PI))bet-=2*PI; if(small(bet,0))bet+=2*PI; op+=bet*r; ed-=r*r*sin(bet); ed+=r*r*bet; } else op+=(ans[i].c-ans[i+1].c).length(); } printf("%.3lf\n%.3lf\n",op,ed/2); }
Time: 2017-10-30