XJHS NOI训练题7 简要题解

A. 填填填

题目

题目描述:
fItyJb4Hm1AAAAAElFTkSuQmCC

输入格式:
这里写图片描述

输出格式:
ImS3QLyqV7AAAAABJRU5ErkJggg==

样例输入:
3
0 3 0
2 0 0
样例输出:
2
数据范围:
这里写图片描述
时间限制:
0.2s
空间限制:
512MB

思路

此题来自于2014年百度之星初赛的Grids一题。原题目没有在初始的空格里填入任何数字,答案为 catalan(n)

为什么呢?注意到,前 i 个数字一定是填在最前面的列里(如果有数字填在后面,最前面的列里不是连续的,有空格的话,之后只能用大于i的数字来填这个空格,这样的话,就一定无法满足每一行列上递增,每一列第一行小于第二行的限制了),而且已经填入的格子里,第一行的格子数大于等于第二行的格子数(若第一行格子数小于第二行格子数,以后无论在第一行空缺的位置填入什么数字,都是大于 i 的,就无法满足每一列第一行比第二行数字小的限制),简而言之,填入数字1 i 后,整个棋盘会是这样的
?????0000
???000000

这非常类似于catalan数里的一个模型:给你一个nn的棋盘,要你从 (0,0) 开始,每次要么往上走一格,要么往右边走一格,且永远不越过对角线,问走到 (n,n) 的方案数。即给你一个数对 (a,b) ,初始为 (0,0) ,每次 (a,b) 可转化为 (a+1,b) (a,b+1) ,且要时刻保证 a>=b ,问变成 (n,n) 的方案数。
这里写图片描述

(似乎扯远了。。。不过补充一下原题的解法,对做出这个题还是有好处的)

此题由于已经填入了某些数字,因此单纯用组合数学来解决此题是不行的。考虑DP,用 f[i][j](j>=i+12) 来表示数字 1 i,第一行最左边填入了 j 个数字的方案数。可以得到dp方程:

f[i][j]=f[i1][j]+f[i1][j1],1j2j(ji)

f[i][j]=f[i1][j],2ji(i2j)

f[i][j]=f[i1][j1],1ji(i1j)

其实也就是对原题的DP解法,加上一些限制而已。

然后此题最丧病的地方要数高精度了,此题时限太小,很容易卡时,因此需要卡常数写个比较快的压位高精才行

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 1000 //数字长度
#define SYS 1000000000000000000LL //进制

using namespace std;

typedef long long int LL;

struct Hugeint //高精度
{
 LL s[60];
 int siz; //数字大小
 Hugeint()
 {
     siz=0;
     memset(s,0,sizeof(s));
 }
 Hugeint operator + (Hugeint b) //高精度加法
 {
  int i,j;
  Hugeint out;
  out.siz=max(siz,b.siz);
  for(i=1;i<=out.siz;i++)
  {
    out.s[i]+=s[i]+b.s[i];
    if(out.s[i]>=SYS)
    {
        out.s[i+1]++;
        out.s[i]-=SYS;
    }
  }
  if(out.s[out.siz+1]!=0)
  {
   out.siz++;
  }
  return out;
 }
 void print() //输出
 {
  int i;
  if(!siz)
  {
      printf("0");
      return;
  }
  printf("%lld",s[siz]);
  for(i=siz-1;i>=1;i--)
  {
   printf("%018lld",s[i]);
  }
 }
}f[2][1100];

int mp[2][1100];
pair<int,int>pos[2200];
int n;

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=2;i++)
    {
        for(int j=1;j<=n;j++)
        {
            int x;
            scanf("%d",&x);
            if(x)
            {
                pos[x]=make_pair(i,j);
            }
        }
    }
    f[0][0].siz=1;
    f[0][0].s[1]=1;
    int now=1,pre=0;
    for(int i=1;i<=2*n;i++)
    {
        memset(f[now],0,sizeof(f[now]));
        for(int j=(i+1)>>1;j<=i&&j<=n;j++)
        {
            if(pos[i]==make_pair(0,0))
                 f[now][j]=f[pre][j-1]+f[pre][j];
            else
            {
                if(pos[i]==make_pair(1,j))
                    f[now][j]=f[pre][j-1];
                else if(pos[i]==make_pair(2,i-j))
                    f[now][j]=f[pre][j];
            }
        }
        swap(now,pre);
    }
    f[pre][n].print();
    return 0;
}

B. 线线线

题目

题目描述:
B+PS758UZatNAAAAABJRU5ErkJggg==

输入格式:
B9KoSW12e+mtAAAAAElFTkSuQmCC

输出格式:
X8zPBSMHuk4AAAAASUVORK5CYII=

样例输入:
8
3 3
3 1
9 1
7 4
10 4
4 5
1 6
5 9
8 8
样例输出:
4.472
数据范围:
这里写图片描述
时间限制:
5s
空间限制:
512MB

思路

我的做法就是直接三分穿过点 p <script type="math/tex" id="MathJax-Element-3422">p</script>的直线的极角。。。
不太好造数据卡三分,但是jcvb的数据还是比较刁,炸了我三分的精度。。。
这个题三分EPS要设1e-13才行,刚开始我设1e-6只有10分,非常悲剧。。

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <cmath>

#define MAXN 220000
#define PI 3.1415926535897384626
#define EPS 1e-13

using namespace std;

typedef long double LD;

struct Point
{
    double x,y;
    Point(){}
    Point(double _x,double _y):x(_x),y(_y){}
}points[MAXN],p;

int n;

Point operator-(Point a,Point b)
{
    return Point(a.x-b.x,a.y-b.y);
}

Point operator+(Point a,Point b)
{
    return Point(a.x+b.x,a.y+b.y);
}

double cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

double dist(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

struct Line
{
    Point st,ed;
    Line(){}
    Line(Point _st,Point _ed):st(_st),ed(_ed){}
};

double dist(Line a,Point b)
{
    return fabs(cross(b-a.st,a.ed-a.st))/dist(a.ed,a.st);
}

double calc(double ang)
{
    Line t=Line(p,p+Point(cos(ang),sin(ang)));
    double maxdis=0;
    for(int i=1;i<=n;i++)
        maxdis=max(maxdis,dist(t,points[i]));
    return maxdis;
}

int main()
{
    scanf("%d",&n);
    scanf("%lf%lf",&p.x,&p.y);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf",&points[i].x,&points[i].y);
    double lowerBound=0,upperBound=PI;
    while(fabs(upperBound-lowerBound)>EPS)
    {
        double mid1=lowerBound+(upperBound-lowerBound)/3;
        double mid2=upperBound-(upperBound-lowerBound)/3;
        if(calc(mid1)<calc(mid2))
            upperBound=mid2;
        else lowerBound=mid1;
    }
    double ans=1e20;
    double L=max((double)0,lowerBound-0.05),R=min(PI,upperBound+0.05);
    ans=min(ans,calc((lowerBound+upperBound)/2));
    double mint=(lowerBound+upperBound)/2;
    for(double t=L;t<=R;t+=0.001)
    {
        if(ans>calc(t))
        {
            ans=calc(t);
            mint=t;
        }
    }
    L=max((double)0,mint-0.005),R=min(PI,mint+0.005);
    for(double t=L;t<=R;t+=0.0001)
    {
        if(ans>calc(t))
        {
            ans=calc(t);
            mint=t;
        }
    }
    long long int t=ans*1000;
    printf("%.3lf",(double)t/1000);
    return 0;
}

C. 凸凸凸

题目

题目描述:
S5SBBKQgAQkIAEJSEACEpCAfwr4f8Lon+6KWgISk

输入格式:
这里写图片描述
输出格式:
GWqRkSAgCAgCgoAgIAgIAoKAICAICALfFwGhMH5f

样例输入:
9 7
1 1
1 3
3 3
3 1
6 5
6 6
7 3
3
0 4 0 4
2 7 0 7
3 7 3 6
样例输出:
4.0
10.0
6.0
数据范围:
这里写图片描述
时间限制:
5s
空间限制:
512MB

思路

我不会写标解。。。直接打了40分的裸暴力

代码

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>

#define MAXN 11000

using namespace std;

typedef long long int LL;
 __attribute__((optimize("-O2")))
int n,k,q;

struct Point
{
    LL x,y;
    Point(){}
    Point(LL _x,LL _y):x(_x),y(_y){}
}points[MAXN],sta[MAXN],tmp[MAXN];

inline Point operator-(Point a,Point b)
{
    return Point(a.x-b.x,a.y-b.y);
}

inline Point operator+(Point a,Point b)
{
    return Point(a.x+b.x,a.y+b.y);
}

inline LL cross(Point a,Point b)
{
    return a.x*b.y-a.y*b.x;
}

inline bool cmp(Point a,Point b)
{
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}

int top=0,tot=0;

inline double Graham()
{
    top=0;
    sort(tmp+1,tmp+tot+1,cmp);
    for(int i=1;i<=tot;i++)
    {
        while(top>=2&&cross(tmp[i]-sta[top-1],sta[top]-sta[top-1])>=0) top--;
        sta[++top]=tmp[i];
    }
    int tmptop=top;
    for(int i=tot-1;i>=1;i--)
    {
        while(top>=tmptop+1&&cross(tmp[i]-sta[top-1],sta[top]-sta[top-1])>=0) top--;
        sta[++top]=tmp[i];
    }
    top--;
    double sqr=0;
    for(int i=1;i<=top;i++)
        sqr+=(double)cross(sta[i],sta[i%top+1]);
    return sqr/(double)2;
}

int main()
{
    scanf("%d%d",&k,&n);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld",&points[i].x,&points[i].y);
    scanf("%d",&q);
    while(q--)
    {
        LL a,b,c,d;
        scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
        tot=0;
        for(int i=1;i<=n;i++)
            if(a<=points[i].x&&points[i].x<=b&&c<=points[i].y&&points[i].y<=d)
                tmp[++tot]=points[i];
        //cout<<Graham()<<endl;
        printf("%.1lf\n",Graham());
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值