codeforces #365 div2ABC题解
水题防止爆零的,不解释了,比较大小的水题
B题是一个数学容斥题,因为数据量比较大,需要用前缀和+容斥的思想来做
题意:n个点,1,2,3,4,……n初始化的时候成环形连边,也就是1-2,2-3,3-4……,n-1
然后,有k个重要城市,每个重要城市与其他所有城市都有一条边
任意两个城市之中,最多有一条边相连
每个点有自己的价值ci,每条边的价值是所连两个点i,j的价值乘积ci*cj
如何容斥?
特殊情况:相邻的!记得特判
如何减小时间复杂度:利用前缀和:两个前缀和
第一个前缀和,是所有的ci之和,需要算进来,每当有一个重要城市的时候,先把重要城市的价值*该前缀和
第二个前缀和,是重要城市的价值ci之和,因为,对于第M+1个重要城市来说,前M个重要城市已经和自己连过边了,那么需要减掉
特殊情况:相邻的,特判!
如果当前的重要城市和前一个是相邻的,那么需要加回来
如果重要城市中包括1和n的话,那么需要加回来
因为,题目中强调了,给定的重要城市序号是按照升序给定的
那么不需要用数组记录,如果1和n的组合存在,那么一定是第一个和最后一个
如果出现相邻的,那么需要记录前一个重要城市的序号
另外,一定要记得初始化!
因为在第一个重要城市之前,是没有重要城市的,那么初始化需要一个跟第一个重要城市无关的值
代码如下:
#include<bits/stdc++.h>
#include<iostream>
using namespace std;
const int maxn=100050;
int n,k,x;
__int64 a[maxn];
__int64 ans,sum;
__int64 desc;
int Start;
int End;
int before;
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d%d",&n,&k)!=EOF){
sum=0;
ans=0;
desc=0;
for(int i=0;i<n;i++){
scanf("%I64d",&a[i]);
sum=sum+a[i];
}
for(int i=0;i<n;i++)
ans+=a[i]*a[(i+1)%n];
//cout<<ans<<endl;
x=-99999;
for(int Case=1;Case<=k;Case++){
before=x;
scanf("%d",&x);
x--;
if (Case==1) Start=x;
else if (Case==k) End=x;
ans+=a[x]*sum;//ALL POINT
ans=ans-(a[x]+a[(x+1)%n]+a[(x+n-1)%n])*a[x];
ans=ans-desc*a[x];
if (before+1==x) ans+=a[before]*a[x];
desc+=a[x];
}
if (End==n-1&&Start==0) ans+=a[0]*a[n-1];
cout<<ans<<endl;
//cout<<endl;
}
return 0;
}
首先知道这个题,是个计算几何
然后呢,肯定需要特判:到底能不能冲过去,是吧
如果对二分熟悉的呢,这个题就过了:因为可以对时间二分,再次判断:人在原点等待一段时间之后,到底能不能冲过去!
思路很连续,代码也很好写,列出一个比例式子,比较大小就好了
代码如下:
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-9;
const int maxn=10050;
int zero(double x){
if (x>eps) return 1;
else if (x<-eps) return -1;
else return 0;
}
double u,w,v;
int n;
struct node{
double x,y;
}p[maxn];
bool judge1(){
/*
u p[i].y
--- ? --------
v p[i].x
*/
for(int i=0;i<n;i++)
if (zero(u*p[i].x-v*p[i].y)<0) return 0;
return 1;
}
bool judge2(double t){
/*
u p[i].y
--- ? -------------
v p[i].x-v*t
*/
for(int i=0;i<n;i++)
if (zero(u*p[i].x-v*p[i].y-u*v*t)>0) return 0;
return 1;
}
int main(){
//freopen("input.txt","r",stdin);
while(scanf("%d%lf%lf%lf",&n,&w,&v,&u)!=EOF){
for(int i=0;i<n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
if (judge1()) printf("%.10lf\n",w/u);
else{
double l=0,r=2e9;
for(int i=0;i<100;i++){
double mid=(l+r)/2;
if (judge2(mid)) r=mid;
else l=mid;
}
printf("%.10lf\n",w/u+(l+r)/2.0);
}
}
return 0;
}
D题
看到就知道是线段树或者树状数组的搞法
贴个链接以后来学习