2022 1-12

2017WF Airport Construction

题意


逆时针给一个简单多边形(不一定为凸多边形)的每一条边,问内部可放置的最长线段的长度。数据保证无两条边共线。

思路:

 

首先,由于不一定是凸多边形,直接用凸包旋转卡壳求直径方法是不对的。

考虑最长的线段何时取到 假设线段不过两个顶点,其长度总可以通过继续旋转线段使其碰到顶点而继续增大。

由反证法可证线段一定过两个顶点。那么线段端点一定为顶点吗?不一定。

上图即是一个反例。

考虑到点的个数<=200,这个规模O(n^4)复杂度可能都不会TLE,自然想到枚举多边形的每两点,连成直线,求直线在多边形内连续的每条线段,更新答案。

问题转化为怎样求直线被多边形切后的连续线段长度。可以采用多边形与直线的交点作为切分点,求每个切分点之间的线段。

 

由于边以逆时针顺序给出,故可判断每条边的两端点和直线(遍历任意俩顶点)的位置关系。

若两点在直线异侧或者一点在直线上,则此边将直线切断,交点作为切分点;(交点放入vector数组v中)

若两点在直线同侧,无任何影响;(不与直线相交)

若两点均在直线上,相当于在直线上多了两个切分点。

在得到一系列切分点之后(该直线被一系列切点切割),根据坐标排个序,遍历判断相邻切分点之间的每条线段在多边形内还是多边形外(用线段中点是否在多边形内)

如果在多边形内,局部答案加上这条线段长度,如果在多边形外,更新全局答案,局部答案清零。

 

代码:

 

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
static const int maxn = 100010;
static const int INF = 0x3f3f3f3f;
static const int mod = (int)1e9 + 7;
static const double eps = 1e-8;
static const double pi = acos(-1);

int n;
double ans = 0;
inline int sgn(double x){
    return fabs(x)<eps?0:(x>0)?1:-1;
}
struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x,y = _y;
    }
    bool operator ==(const Point &b)const{
        return (sgn(x-b.x)==0)&&(sgn(y-b.y)==0)?true:false;
    }
    bool operator <(const Point &b)const{
        return (sgn(x-b.x)==0)?(y < b.y):(x < b.x);
    }
    Point operator -(const Point &b)const{
        return Point(x-b.x,y-b.y);
    }
    double operator ^(const Point &b)const{
		return x*b.y - y*b.x;
	}
    double operator *(const Point &b)const{
        return x*b.x + y*b.y;
    }
}p[210];

struct Line{
    Point s,t;
    Line(){}
    Line(Point _s,Point _t){
        s = _s,t = _t;
    }
    Point crosspoint(Line v){
    	double a1 = (v.t-v.s)^(s-v.s);
    	double a2 = (v.t-v.s)^(t-v.s);
    	return Point((s.x*a2-t.x*a1)/(a2-a1),(s.y*a2-t.y*a1)/(a2-a1));
    }
    bool pointonseg(Point q){
	   return sgn((q-s)^(t-s)) == 0 && sgn((q-s)*(q-t)) <= 0;
    }
};
 bool pointonpoly(Point q){
    int res = 0;
    for(int i = 0;i < n;i++){
        if(Line(p[i],p[i+1]).pointonseg(q))return true;
        int d1 = sgn(p[i].y - q.y),
            d2 = sgn(p[i+1].y - q.y),
            k = sgn((p[i+1] - p[i])^(q - p[i]));
        if (k > 0 && d1 <= 0 && d2 > 0)res++;
        if (k < 0 && d2 <= 0 && d1 > 0)res--;
    }
    return res ? true : false;
}
 double dis( Point a, Point b){
    return sqrt((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y));
}

 void attempt( int p1, int p2){
    vector<Point>v;
    Line l(p[p1],p[p2]);
    for(int i = 0;i < n;i++){
        if(sgn((l.t-l.s)^(p[i]-l.s)) * sgn((l.t-l.s)^(p[i+1]-l.s)) <= 0){ //该相邻点构成的线段在直线两侧
            Point v1 = l.t - l.s,v2 = p[i+1] - p[i];
            if(sgn(v1^v2) == 0){ // 与直线重合
                v.push_back(p[i]);
                v.push_back(p[i+1]);
            }
            else v.push_back(l.crosspoint(Line(p[i],p[i+1])));  // 线段p[i] p[i+1]与 直线l 交点压入v[];
        }
    }
    sort(v.begin(),v.end());
    v.resize(unique(v.begin(),v.end()) - v.begin());
    int cnt = v.size();
    double res = 0;
    for(int i = 1;i < cnt;i++){
        if(pointonpoly(Point((v[i-1].x+v[i].x)/2.0,(v[i-1].y+v[i].y)/2.0)))//判断中点是否在多边形内
        res += dis(v[i-1],v[i]);
        else{
            res = 0;
            if(dis(v[i],v.back()) <= ans)return;
        }
        ans = max(ans,res);
    }
}
int main(){
    scanf("%d",&n);
    for(int i = 0;i < n;i++)scanf("%lf %lf",&p[i].x,&p[i].y);
    p[n].x = p[0].x,p[n].y = p[0].y;
    for(int i = 0;i < n;i++)
    for(int j = i+1;j < n;j++)
    attempt(i,j);
    printf("%.9f\n",ans);
    return 0;
}

质数行者 DP

 

主要是公式!

 

#include <iostream>
#include <bits/stdc++.h>
#define ll long long
const int N=2005;
const ll mod=1000000007;
using namespace std;

/*
1.先打表2005以内的阶乘 和 逆元; 方便套公式 p*(x+y+z)!/(x!+y!+z!)
2.处理dp[][] dp[1][0]=1; dp[i][j+1]= (dp[i][j+1] + dp[i-prime[k]][j] )%mod; 不同的i-prime[]( j步)可以到达i dp[i][j+1]答案多一

*/

ll vis[N][N],dp[N][N>>1],prime[N],tmp[N];
int tot=0;
ll fac[N],inv[N];
ll qk(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) ans=(a*ans)%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
ll inv_(ll x){
    return qk(x,mod-2);
}

ll calc(int x,int y,int z){//从(1,1,1)出发到达(x,y,z)的方案数
    memset(tmp,0,sizeof(tmp));
    for (int i=0;i<=y;i++){
        for (int j=0;j<=z;j++){
            tmp[i+j]+=dp[y][i]*dp[z][j]%mod*fac[i+j]%mod*inv[i]%mod*inv[j]%mod;
            tmp[i+j]%=mod;
        }
    }//合并二三维
    ll ans=0;
    for (int i=0;i<=x;i++){
        for (int j=0;j<=y+z;j++){
            ans+=dp[x][i]*tmp[j]%mod*fac[i+j]%mod*inv[i]%mod*inv[j]%mod;
            ans%=mod;
            //cout<<ans<<endl;
        }
    }
    return ans;
}
int v[N];
void init(){
    v[0]=v[1]=1;
    for(int i=2;i<=N-100;i++){
        if(v[i]==0) prime[++tot]=i,v[i]=1;
        for(int j=1;i*prime[j]<=N-100&&j<=tot;j++){
            v[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
    fac[1]=1,fac[0]=1;
    for(int i=2;i<=N-10;i++)
        fac[i]=fac[i-1]*i%mod;
    inv[N-10]=inv_(fac[N-10]);
    for(int i=N-11;i>=0;i--)
     inv[i]=inv[i+1]*(i+1)%mod;
}



int main()
{
   init();
   //int vis[N][N];
   dp[1][0]=1; vis[1][0]=1;
   int n,m,w,r1,c1,h1,r2,c2,h2,cnt=0;
   cin>>n>>m>>w;
   cin>>r1>>c1>>h1>>r2>>c2>>h2;
   int maxn=max(max(n,m),w);
   for(int i=2;i<=maxn;i++){
    for(int j=0;j<=cnt;j++){ //走到i的步数
     for(int k=1;k<=tot;k++){ //打表得到tot个质数prime[]
       if(i-prime[k]<=0) break; 
       if(!vis[i-prime[k]][j]) continue;
       vis[i][(j+1)]=1;
       dp[i][j+1]=(dp[i][j+1] + dp[i-prime[k]][j] )%mod;

    }
    }
     if(vis[i][cnt+1]==1)cnt++;
   }

   ll ans=calc(n,m,w);
    ans=(ans-(calc(r1,c1,h1)*calc(n-r1+1,m-c1+1,w-h1+1))%mod+mod)%mod;
    ans=(ans-(calc(r2,c2,h2)*calc(n-r2+1,m-c2+1,w-h2+1))%mod+mod)%mod;
    if (r1<=r2&&c1<=c2&&h1<=h2)ans=(ans+calc(r1,c1,h1)*calc(r2-r1+1,c2-c1+1,h2-h1+1)%mod*calc(n-r2+1,m-c2+1,w-h2+1)%mod)%mod;
    if (r2<=r1&&c2<=c1&&h2<=h1)ans=(ans+calc(r2,c2,h2)*calc(r1-r2+1,c1-c2+1,h1-h2+1)%mod*calc(n-r1+1,m-c1+1,w-h1+1)%mod)%mod;
    printf("%lld\n",ans);

    return 0;
}
/*
5 6 1
3 4 1 1 2 1
*/

 

posted @ 2022-01-12 22:59  GGood_Name  阅读(100)  评论(0)    收藏  举报