暴力专场, \(T1\) 写了一个 \(n^42^n\) 的做法本地跑 \(1.6s\),交上去 \(n=15\) 的全 \(T\)了,剩下的两道题和预期的一样。
把前 \(k\) 个人称为 \(A\) 组,把后 \(n-k\) 个人称为 \(B\) 组。
那么方案希望 \(A\) 组中的最低分大于等于 \(B\) 组中的最高分。
去枚举 \(A\) 组中的最低分 \(x\),这个最低分最多只有 \(n^2\) 种情况。
实现一个函数 \(f(l,r)\) 表示 \(A\) 组中的分数都大于等于 \(l\),\(B\) 组中的分数都小于等于 \(r\) 的方案。
为了去重,需要强制 \(A\) 组中有一个人是最低分。
那么答案就是 \(\sum_{x}f(x,x)-f(x+1,x)\)
对于 \(c\) 数组按照权值从小到大排序。
设 \(cnt1[i]\) 表示 \(c[i]\) 能够匹配 \(A\) 中的 \(cnt1[i]\) 个元素,\(cnt2[i]\) 表示 \(c[i]\) 能够匹配 \(B\) 中的 \(cnt2[i]\) 个元素。
随着 \(c\) 的增大,\(cnt1\) 会增大,\(cnt2\) 会减小。
也就是说 \(c[i]\) 在 \(A\) 组中能够匹配上的,\(c[i+1]\) 一定能够匹配上, \(c[i]\) 在 \(B\) 组中能够匹配上的,\(c[i-1]\) 一定能够匹配上。
所以对于 \(A\) 组的方案,在 \(c\) 数组中正序考虑,对于 \(B\) 组的方案,在 \(c\) 数组中倒序考虑。
因为之前匹配的 \(A\) 在之后不能匹配,所以 \(c[x]\) 第 \(i\) 个匹配上 \(A\) 的方案为 \(cnt1[x]-(i-1)\),\(B\) 数组同理。
实现的时候设 \(dp[i][j]\) 表示 \(c\) 数组中的前 \(i\) 个数已经有 \(j\) 个匹配到了 \(A\) 数组中的方案数,最终的答案就是 \(dp[n][k]\)。
时间复杂度 \(n^4\)。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#define rg register
template<typename T>void read(rg T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<‘0‘ || ch>‘9‘){
if(ch==‘-‘) fh=-1;
ch=getchar();
}
while(ch>=‘0‘ && ch<=‘9‘){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=105,mod=1e9+7;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
int n,k,a[maxn],b[maxn],ans,f[maxn][maxn],sta[maxn*maxn],tp,cnt1[maxn],cnt2[maxn];
int getf(rg int l,rg int r){
memset(cnt1,0,sizeof(cnt1));
memset(cnt2,0,sizeof(cnt2));
memset(f,0,sizeof(f));
for(rg int i=1;i<=k;i++){
for(rg int j=1;j<=n;j++){
if(a[i]+b[j]>=l) cnt1[j]++;
}
}
for(rg int i=k+1;i<=n;i++){
for(rg int j=1;j<=n;j++){
if(a[i]+b[j]<=r) cnt2[j]++;
}
}
f[0][0]=1;
for(rg int i=0;i<n;i++){
for(rg int j=0;j<=i && j<=k;j++){
if(cnt1[i+1]>j) f[i+1][j+1]=addmod(f[i+1][j+1],mulmod(f[i][j],(cnt1[i+1]-j)));
if(cnt2[i+1]>n-k-(i+1-j)) f[i+1][j]=addmod(f[i+1][j],mulmod(f[i][j],(cnt2[i+1]-(n-k-(i+1-j)))));
}
}
return f[n][k];
}
int main(){
read(n),read(k);
for(rg int i=1;i<=n;i++) read(a[i]);
for(rg int i=1;i<=n;i++) read(b[i]);
std::sort(a+1,a+n+1);
std::reverse(a+1,a+n+1);
std::sort(b+1,b+n+1);
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n;j++){
sta[++tp]=a[i]+b[j];
}
}
std::sort(sta+1,sta+tp+1);
tp=std::unique(sta+1,sta+tp+1)-sta-1;
for(rg int i=1;i<=tp;i++){
ans=addmod(ans,delmod(getf(sta[i],sta[i]),getf(sta[i]+1,sta[i])));
}
printf("%d\n",ans);
return 0;
}
用 \(st\) 表预处理出第 \(2^i\) 步后,以 \(j\) 为左端点,长度为 \(2^k\) 的区间能够向左和向右达到的最远的坐标。
二分一个答案 \(mids\) 表示是不是存在两个点需要大于 \(mids\) 步才能到达。
设 \(L(i,x)\)表示 \(i\) 走 \(x\) 步的左区间边界, \(R(i,x)\) 同理。
那么就发现存在距离大于 \(mids\) 的点对 \((a,b)(a<b)\),等价于存在 \(L(b,mids)>a,R(a,mids)<b\)。
维护前缀后缀最值即可快速判断。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define rg register
template<typename T>void read(rg T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<‘0‘ || ch>‘9‘){
if(ch==‘-‘) fh=-1;
ch=getchar();
}
while(ch>=‘0‘ && ch<=‘9‘){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=2e5+5;
int n,a[maxn],lg[maxn],mx[18][18][maxn],mn[18][18][maxn];
void getans(rg int id,rg int l,rg int r,rg int &x,rg int &y){
rg int k=lg[r-l+1];
x=std::min(mn[id][k][l],mn[id][k][r-(1<<k)+1]);
y=std::max(mx[id][k][l],mx[id][k][r-(1<<k)+1]);
}
int mmin[maxn],mmax[maxn],fl[maxn],fr[maxn];
bool jud(rg int mids){
rg int len,k;
for(rg int i=1;i<=n;i++){
len=mids,k=0;
fl[i]=fr[i]=i;
while(len){
if(len&1) getans(k,fl[i],fr[i],fl[i],fr[i]);
len>>=1,k++;
}
}
mmin[0]=n+1;
for(rg int i=1;i<=n;i++) mmin[i]=fr[i],mmax[i]=fl[i];
for(rg int i=1;i<=n;i++) mmin[i]=std::min(mmin[i-1],mmin[i]);
for(rg int i=n;i>=1;i--) mmax[i]=std::max(mmax[i+1],mmax[i]);
for(rg int i=1;i<=n;i++){
if(mmin[fl[i]-1]<i) return 1;
if(mmax[fr[i]+1]>i) return 1;
}
return 0;
}
int main(){
read(n);
for(rg int i=1;i<=n;i++) read(a[i]);
for(rg int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
for(rg int i=1;i<=n;i++){
mx[0][0][i]=std::min(n,i+a[i]);
mn[0][0][i]=std::max(1,i-a[i]);
}
for(rg int i=1;i<=17;i++){
for(rg int j=1;j+(1<<i)-1<=n;j++){
mn[0][i][j]=std::min(mn[0][i-1][j],mn[0][i-1][j+(1<<(i-1))]);
mx[0][i][j]=std::max(mx[0][i-1][j],mx[0][i-1][j+(1<<(i-1))]);
}
}
for(rg int j=1;j<=n;j++){
getans(0,mn[0][0][j],mx[0][0][j],mn[1][0][j],mx[1][0][j]);
}
for(rg int i=1;i<=lg[n];i++){
for(rg int j=1;j<=n;j++){
getans(i-1,mn[i-1][0][j],mx[i-1][0][j],mn[i][0][j],mx[i][0][j]);
}
for(rg int j=1;j<=17;j++){
for(rg int k=1;k+(1<<j)-1<=n;k++){
mn[i][j][k]=std::min(mn[i][j-1][k],mn[i][j-1][k+(1<<(j-1))]);
mx[i][j][k]=std::max(mx[i][j-1][k],mx[i][j-1][k+(1<<(j-1))]);
}
}
}
rg int l=1,r=n,mids;
while(l<=r){
mids=(l+r)>>1;
if(jud(mids)) l=mids+1;
else r=mids-1;
}
printf("%d\n",r+1);
return 0;
}
用割圆法把圆分成正 \(2000\) 边形,正常跑半平面交即可。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cmath>
#include<queue>
#define rg register
const int maxn=1e5+5,maxcnt=2000;
const double pi=acos(-1),INF=1e3,eps=1e-16;
inline int dcmp(rg double now){
return std::fabs(now)<=eps?0:(now>0?1:-1);
}
struct Node{
double x,y;
Node(){}
Node(rg double aa,rg double bb){
x=aa,y=bb;
}
friend Node operator +(const Node& A,const Node& B){
return Node(A.x+B.x,A.y+B.y);
}
friend Node operator -(const Node& A,const Node& B){
return Node(A.x-B.x,A.y-B.y);
}
friend Node operator *(const Node& A,const double& B){
return Node(A.x*B,A.y*B);
}
friend Node operator /(const Node& A,const double& B){
return Node(A.x/B,A.y/B);
}
friend double operator *(const Node& A,const Node& B){
return A.x*B.x+A.y*B.y;
}
friend double operator ^(const Node& A,const Node& B){
return A.x*B.y-B.x*A.y;
}
double angle(){
return atan2(y,x);
}
}q2[maxn],jl[maxn];
struct Line{
Node s,t;
int id;
double ang;
Line(rg Node aa=Node(),rg Node bb=Node(),rg int cc=0){
s=aa,t=bb,ang=(bb-aa).angle(),id=cc;
}
friend bool operator <(const Line& A,const Line& B){
double r=A.ang-B.ang;
if(dcmp(r)==0) return dcmp((A.t-A.s)^(B.t-A.s))==-1;
return dcmp(r)==-1;
}
friend double operator *(const Line& A,const Line& B){
return (A.t-A.s)^(B.t-B.s);
}
}q1[maxn],sta[maxn];
bool judline(rg Line aa,rg Line bb){
return dcmp(aa*bb)==0;
}
bool onright(rg Line aa,rg Node bb){
return dcmp((aa.t-aa.s)^(bb-aa.s))==-1;
}
Node getsi(rg Line aa,rg Line bb){
return aa.s+(aa.t-aa.s)*(((bb.t-bb.s)^(aa.s-bb.s))/(aa*bb));
}
double getarea(rg Node aa,rg Node bb){
return sqrt((aa.x-bb.x)*(aa.x-bb.x)+(aa.y-bb.y)*(aa.y-bb.y));
}
int head,tail,tp;
double ans1,ans2;
void SI(){
std::sort(sta+1,sta+tp+1);
head=tail=0;
q1[0]=sta[1];
for(rg int i=2;i<=tp;i++){
if(dcmp(sta[i].ang-sta[i-1].ang)!=0){
while(head<tail && onright(sta[i],q2[tail-1])) tail--;
while(head<tail && onright(sta[i],q2[head])) head++;
q1[++tail]=sta[i];
if(head<tail) q2[tail-1]=getsi(q1[tail],q1[tail-1]);
}
}
while(head<tail && onright(q1[head],q2[tail-1])) tail--;
while(head<tail && onright(q1[tail],q2[head])) head++;
q2[tail]=getsi(q1[head],q1[tail]);
for(rg int i=head+1;i<=tail;i++){
if(q1[i].id==1) ans2+=getarea(q2[i],q2[i-1]);
else ans1+=getarea(q2[i],q2[i-1]);
}
if(q1[head].id==1) ans2+=getarea(q2[head],q2[tail]);
else ans1+=getarea(q2[head],q2[tail]);
}
int n,r;
int main(){
scanf("%d%d",&n,&r);
rg double aa,bb;
rg int cc;
for(rg int i=1;i<=maxcnt;i++){
aa=cos(2.0*i*pi/maxcnt)*r;
bb=sin(2.0*i*pi/maxcnt)*r;
jl[i]=Node(aa,bb);
}
for(rg int i=1;i<maxcnt;i++) sta[++tp]=Line(jl[i],jl[i+1],1);
sta[++tp]=Line(jl[maxcnt],jl[1],1);
sta[++tp]=Line(Node(-INF,-INF),Node(INF,-INF),0);
sta[++tp]=Line(Node(INF,-INF),Node(INF,INF),0);
sta[++tp]=Line(Node(INF,INF),Node(-INF,INF),0);
sta[++tp]=Line(Node(-INF,INF),Node(-INF,-INF),0);
rg Node dd,ee;
rg double tmp1,tmp2;
for(rg int i=1;i<=n;i++){
scanf("%d%lf",&cc,&bb);
if(bb==r) continue;
tmp1=1.0*cc/180*pi;
tmp2=acos(bb/r);
tmp2=tmp1-tmp2;
dd=Node(cos(tmp2)*r,sin(tmp2)*r);
ee=Node(cos(tmp1)*bb,sin(tmp1)*bb);
sta[++tp]=Line(dd,ee);
}
SI();
printf("%.10f %.10f\n",ans1,ans2);
return 0;
}
原文:https://www.cnblogs.com/liuchanglc/p/14533658.html