持续雷击
比赛地址
Pro : 3/5/11
Rank: 312/1159
[B] Boundary
题意
给出平面上n个点,找到一个过原点的圆,使得其能覆盖最多的点.
题解
因为已知原点在圆上,所以只需要枚举两个点就可以确定一个圆.枚举出所有的组合,算出共\frac{n(n-1)}{2}个圆心.找到其中出现次数最多的圆心,设出现了k次,则答案一定是C_{Ans}^{2}=k的解,这是显然的.总的时间复杂度为O(n^{2}logn).
本题卡精度,eps需要设到1e-10.
代码
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <unordered_map>
#define IL inline
#define LL int
#define ULL unsigned LL
#define _BS 1048576
char _bf[_BS];
char *__p1=_bf,*__p2=_bf;
#define _IO char *_p1=__p1,*_p2=__p2;
#define _OI __p1=_p1,__p2=_p2;
#define GC (_p1==_p2&&(_p2=(_p1=_bf)+fread(_bf,1,_BS,stdin),_p1==_p2)?EOF:*_p1++)
#define PC putchar
#define _Q_(x) {register char _c=GC,_v=1;for(x=0;_c<48||_c>57;_c=GC)if(_c==45)_v=-1;
#define _H_(x) for(;_c>=48&&_c<=57;x=(x<<1)+(x<<3)+_c-48,_c=GC);if(_v==-1)x=-x;}
#define sc(x) _Q_(x)_H_(x)
#define se(x) _Q_(x)else if(_c==EOF)return 0;_H_(x)
#define _G1(_1) int _1;sc(_1)
#define _G2(_1,_2) int _1,_2;sc(_1)sc(_2)
#define _G3(_1,_2,_3) int _1,_2,_3;sc(_1)sc(_2)sc(_3)
#define _gG(_1,_2,_3,_get, ...) _get
#define get(...) _gG(__VA_ARGS__,_G3,_G2,_G1, ...)(__VA_ARGS__)
#define F(i,l,r) for(int i=(l);i<(r);++i)
#define FD(i,l,r) for(int i=(l);i<=(r);++i)
#define MIN(a, b) ((a)<(b)?(a):(b))
#define MAX(a, b) ((a)>(b)?(a):(b))
using D = double;
constexpr D eps(1e-10);
constexpr int MN(5e3);
constexpr int sign(D x)
{
return x<-eps ? -1 : (x > eps ? 1 : 0);
}
struct Po
{
D x, y;
int hash() const
{
return 0;
}
bool operator <(const Po & o) const
{
return sign(x-o.x) < 0 || !sign(x-o.x) && sign(y-o.y) < 0;
}
bool operator ==(const Po & o) const
{
return !sign(x-o.x) && !sign(y-o.y);
}
} v[2000005];
struct Poo
{
int x, y;
bool operator ==(const Poo & o) const
{
return x==o.x && y==o.y;
}
};
using namespace std;
int main()
{
unordered_map<int, int> findcn;
Poo p[MN];
int cnt[MN];
_IO
get(n)
if (n <= 1)
return printf("%d\n", n), 0;
for (int i=1; i<=n; ++i)
findcn[i*(i-1)/2] = i;
for (int i=0; i<n; ++i)
{
sc(p[i].x)
sc(p[i].y)
}
int vnt = 0;
const int nn = n-1;
for (int i=0; i<nn; ++i)
{
for (int j=i+1; j<n; ++j)
{
D a = -p[i].x;
D b = -p[i].y;
D c = -p[j].x;
D d = -p[j].y;
D e = (-p[i].x*p[i].x - p[i].y*p[i].y) / (D(2));
D f = (-p[j].x*p[j].x - p[j].y*p[j].y) / (D(2));
D fm = b*c-a*d;
if (!sign(fm))
continue;
v[vnt].x = (b*f-d*e)/fm, v[vnt].y = (c*e-a*f)/fm, ++vnt;
// v.emplace_back(std::move(Po( (b*f-d*e)/fm, (c*e-a*f)/fm )));
}
}
std::sort(v, v+vnt);
int ans = 0;
for (int i=0; i<vnt; )
{
int j;
for (j=i+1; j<vnt&&v[i]==v[j]; ++j);
if (j-i > ans)
ans = j-i;
i = j;
}
printf("%d", findcn[ans]);
return 0;
}
[C] Cover the Tree
题意
给出一棵树,求最少的路径数,使得树上的每条边至少被覆盖了一次.
题解
显然,每条选择的路径一定是以叶子节点为起止点的.如果叶子的数目是n的话,答案就是\frac{n+1}{2}.接下来是如何将这些叶子两两配对.先求出DFS序,设a_{i}表示第i个叶子.只需要让a_{i}与a_{\frac{n}{2}+i}配对即可.对于叶子数是奇数的情况单独判断下就好.
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
#include<queue>
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define IL inline
#define reg register
#define LL long long
#define N 200010
#define INF 0x3f3f3f3f
using namespace std;
int n,i,x,y,lsum=1,cnt,root,ans,tot;
int head[N],d[N],v[N],f[N],size[N],msize[N];
struct Edge{
int t,next;
}e[N*8];
struct Data{
int x,y;
}Ans[N];
IL int read(){
int p=0,f=1; char c=getchar();
while (c<48||c>57) {if (c=='-') f=-1; c=getchar();}
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p*f;
}
inline void Add(int s,int t){
e[lsum].t=t; e[lsum].next=head[s]; head[s]=lsum++;
}
IL void Dfs(int x,int fa){
reg int i=0;
if (d[x]==1) {
v[++v[0]]=x; return;
}
for (i=head[x];i;i=e[i].next) if (e[i].t!=fa) Dfs(e[i].t,x);
}
int main(){
#ifdef __Marvolo
freopen("zht.in","r",stdin);
freopen("zht.out","w",stdout);
#endif
n=read();
if (n==1){
puts("0"); return 0;
}
if (n==2){
puts("1"); puts("1 2"); return 0;
}
for (i=1;i<n;i++){
x=read(); y=read();
d[x]++; d[y]++;
Add(x,y); Add(y,x);
}
for (i=1;i<=n;i++)
if (d[i]>1){
Dfs(i,0);
break;
}
x=v[0]/2;
for (i=1;i<=x;i++){
ans++;
Ans[ans].x=v[i]; Ans[ans].y=v[i+x];
}
if (v[0]&1){
ans++;
Ans[ans].x=v[1]; Ans[ans].y=v[v[0]];
}
printf("%d\n",ans);
for (i=1;i<=ans;i++) printf("%d %d\n",Ans[i].x,Ans[i].y);
return 0;
}
[D] Duration
题意&题解
签到题
代码
h1,m1,s1 = map(int, input().split(':'))
h2,m2,s2 = map(int, input().split(':'))
t1 = h1*60*60 + m1*60 + s1
t2 = h2*60*60 + m2*60 + s2
print(abs(t1-t2))
[F] Fake Maxpooling
题意
给出一个n\times m的方阵,a_{ij}=lcm(i,j).求这个方阵中,每一个k \times k子阵的最大值之和.
题解
预处理出方阵后拿单调队列直接计算即可.
据说算lcm时会卡常,用了一个神奇的gcd写法直接莽过去了.
代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
#define T int
T gcd(T __a, T __b) {
T __tp;
while (__b) {
__tp = __a % __b;
__a = __b;
__b = __tp;
}
return __a;
}
struct node{ int x, y; } v[5050];
int mn1[5050][5050], s[5050][5050], n, m, k;
void getmin() {
for (int t = 1; t <= n; t++) {
int i, head = 1, tail = 0;
for (i = 1; i <= 5000; i++) v[i].x = v[i].y = 0;
for (i = 1; i < k; i++) {
while(head <= tail && v[tail].x <= s[t][i]) tail--;
v[++tail].x = s[t][i], v[tail].y = i;
}
for (; i <= m; i++) {
while (head <= tail && v[tail].x <= s[t][i]) tail--;
v[++tail].x = s[t][i], v[tail].y = i;
while (v[head].y < i-k+1) head++;
mn1[t][i-k+1] = v[head].x;
}
}
for (int t = 1; t <= (m-k+1); t++) {
int i, head = 1, tail = 0;
for (i = 1; i <= 5000; i++) v[i].x = v[i].y = 0;
for (i = 1; i < k; i++) {
while(head <= tail && v[tail].x <= mn1[i][t]) tail--;
v[++tail].x = mn1[i][t], v[tail].y = i;
}
for (; i <= n; i++) {
while(head <= tail && v[tail].x <= mn1[i][t]) tail--;
v[++tail].x = mn1[i][t], v[tail].y = i;
while(v[head].y < i-k+1) head++;
s[i-k+1][t] = v[head].x;
}
}
}
int main() {
for (int i = 1; i <= 5000; i++) for (int j = i; j <= 5000; j++) s[i][j] = i*j/gcd(j, i);
for (int i = 1; i <= 5000; i++) for (int j = 1; j < i; j++) s[i][j] = s[j][i];
scanf("%d%d%d", &n, &m, &k);
getmin();
long long int ans = 0;
for (int i = 1; i <= n-k+1; i++) for (int j = 1; j <= m-k+1; j++)
ans += s[i][j];
cout << ans << endl;
return 0;
}
[G] Greater and Greater
题意
给出一个长度为n的序列和一个长度为m的序列,m<n.求将第二个序列放到原序列的哪些位置,可以让第一个序列的值大于等于第二个序列的值.
题解
bitset的奇妙用法.
预处理出第二个序列中,每个位置可以放在哪里,然后与到一起,再统计一下1的数目就行了.时间复杂度是O(\frac{nm}{64}).
代码
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cmath>
#include<map>
#include<bitset>
#define l(x) (x<<1)
#define r(x) ((x<<1)+1)
#define IL inline
#define reg register
#define LL long long
#define N 150010
#define INF 0x3f3f3f3f
using namespace std;
int n,m,i,cnt,Ans;
int a[N],b[N],l[N*2];
bitset <N> c,v,ans;
struct Data{
int x,y,id;
}p[N*2];
IL int Abs(int x){return (x<0)?-x:x;}
IL void Swap(int &a,int &b){a^=b^=a^=b;}
IL int Min(int a,int b){return (a<b)?a:b;}
IL int Max(int a,int b){return (a>b)?a:b;}
IL int read(){
int p=0,f=1; char c=getchar();
while (c<48||c>57) {if (c=='-') f=-1; c=getchar();}
while (c>=48&&c<=57) p=(p<<1)+(p<<3)+c-48,c=getchar();
return p*f;
}
IL bool cmp(Data x,Data y){return (x.x==y.x)?x.y<y.y:x.x<y.x;}
int main(){
#ifdef __Marvolo
freopen("zht.in","r",stdin);
freopen("zht.out","w",stdout);
#endif
n=read(); m=read();
for (i=1;i<=n;i++) a[i]=read(),l[++l[0]]=a[i];
for (i=1;i<=m;i++) b[i]=read(),l[++l[0]]=b[i];
sort(l+1,l+1+l[0]);
for (i=1;i<=n;i++) a[i]=lower_bound(l+1,l+1+l[0],a[i])-l;
for (i=1;i<=m;i++) b[i]=lower_bound(l+1,l+1+l[0],b[i])-l;
cnt=0;
for (i=1;i<=n;i++){
++cnt;
p[cnt].x=a[i]; p[cnt].y=1; p[cnt].id=i;
}
for (i=1;i<=m;i++){
++cnt;
p[cnt].x=b[i]; p[cnt].y=0; p[cnt].id=i;
}
sort(p+1,p+1+cnt,cmp);
for (i=1;i<=n-m+1;i++) ans[i]=1;
for (i=cnt;i;i--){
if (p[i].y==1){
v[p[i].id]=1; continue;
}
c=v>>(p[i].id-1); ans&=c;
// for (int j=1;j<=n;j++) cout<<c[j];
// cout<<endl;
}
cout<<ans.count()<<endl;
return 0;
}
[H] Happy Triangle
题意
有三种操作,第一种是往一个集合里加入一个数,第二种是删除一个数.第三种是询问操作,给出一个数,问能否从集合中取出两个数,使得这三个数构成一个合法的三角形.
题解
对于给出的数,分最大边和不是最大边讨论.
对于不是最大边的情况,只需要求两个前驱;对于最大边的情况,只需要考虑所有相邻的数就行.证明是显然的.
官方题解需要写一个平衡树,其实可以通过两个set+线段树解决.
代码
待填
[K] Keyboard Free
题意
已知三个同心圆的半径,求在这三个圆上各随机取一个点所构成的三角形的面积的期望.
题解
首先可以固定一个点.当第二个点也固定之后,和第一个点连线,先算出第三个点到该直线的距离的期望,再算面积的期望.因为精度要求不是很高,第二个点可以随机取足够多次.
实际测试发现卡常卡精度QAQ
代码
待填
总结
开题策略大失败.
C题一开始没什么思路,就一直在写B.结果因为没注意看条件,以为会有点重合的情况,自己给自己加了一堆限制条件,代码也改了好几版.发现只需要交初版代码的时候又被卡了精度莫名WA,最后凭直觉改了eps才过去.
因为在B题上浪费了太多时间,后期又一直在死磕C题和K题,导致有些题根本就没有读,错失了过G题和H题的机会.K题思路已经是正确的了,但是剩的时间太短来不及推式子,后期节奏彻底崩塌.
望之后引以为戒QAQ.
本文地址: 2020牛客暑期多校第二场