2017中国大学生程序设计竞赛-哈尔滨站 一些题解

F - Permutation
水题,岔开构造就行
H - A Simple Stone Game
这样考虑:如果一个结果得gcd是x,那么这个x肯定是所有石子和的某个因子,所以遍历所有因子,取模之后模拟就行了
B - K-th Number
将每个大于K的区间的第K大都求出来放到新的数组里面,然后求出新的数组中的第M大。
首先真的求出来所有的区间的第K大是不现实的,也就是说我们要二分一下,我们二分最后的第M大的数,对于这个数,他作为区间的第K大的次数如果大于M,说明我们要增大,反之这个数还可以增大。
如何求出来有多少个区间呢?我们首先已经固定了目前判断的数,那么我们使用尺取,先找到正好满足第K大的区间(K个大于mid的数),这个区间可以往后也就是[r,n] 取到的K大值一定都是大于等于mid的,所以把这一段先加上去,对于每个当前区间小于mid的值,都会使区间的个数增加一次。

#include <bits/stdc++.h>
#define ll long long
#define find fuck
#define next fuckme
using namespace std;
const int MAXN = 1e5 + 5;
int a[MAXN];
int n,k;
ll m;
bool check(int mid)
{

    ll ans  = 0;
    int num = 0;
    int j = 1;
    for(int i = 1;i<=n;i++)
    {
        if(a[i] >= mid) num++;
        if(num == k)
        {
            ans += n - i + 1;
            while(a[j] < mid)
            {
                ans += n - i + 1;
                j++;
            }
            num--;
            j++;
        }
    }

    return ans >= m;

}
int main()
{

    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        scanf("%d%d%lld",&n,&k,&m);
        for(int i = 1;i<=n;i++) scanf("%d",&a[i]);
        int l = 1,r = 1e9 + 7;
        while(l < r)
        {
            int mid = (l + r) >> 1;
            if(check(mid))
            {
                l = mid+1;
            }
            else r = mid;
        }
        printf("%d\n",l-1);
    }
    return 0;
}

A - Palindrome
我靠这题这么些系列……
题意是找到形如 ()i () j () 这样的回文串。也就是以i为中心和以j为中心都是回文的。
我们设p[i]为i位置上的回文半径,我们观察一下这个回文串,可以得出三个关系
i &lt; j , i ≥ j − p j , j ≤ i + p i i &lt; j , i \ge j - p_j ,j \le i +p_i i<j,ijpj,ji+pi
有了这个,我们可以用树状数组维护这个数组,我们先把所有i - p_i 的位置存起来,然后在查询第i个位置的时候,也就是满足第以个式子的值,把这个位置对应的位置都存入树状数组,这样我们就求出来一堆符合的位置,然后我们要满足第一个式子,就查询满足式子1,3的i ~ i + p[i] 这一段的值

#include <bits/stdc++.h>
#define ll long long
#define find fuck
#define next fuckme
using namespace std;
const int MAXN=5e5 + 10;
char Ma[MAXN*2];
int Mp[MAXN*2];
int Manacher(char s[],int len)
{
    int l=0;
    Ma[l++]='$';
    Ma[l++]='#';
    for(int i=0; i<len; i++)
    {
        Ma[l++]=s[i];
        Ma[l++]='#';
    }
    Ma[l]=0;
    int mx=0,id=0;
    for(int i=0; i<l; i++)
    {
        Mp[i]=mx>i?min(Mp[2*id-i],mx-i):1;
        while(Ma[i+Mp[i]]==Ma[i-Mp[i]])
            Mp[i]++;
        if(i+Mp[i]>mx)
        {
            mx=i+Mp[i];
            id=i;
        }
    }
    return l;
}
char s[MAXN];
int bit[MAXN],p[MAXN];
vector<int> g[MAXN];
inline int lowbit(int x)
{
    return x & (-x);
}
int MX;
int sum(int i)
{
    int s = 0;
    while(i>0){
        s += bit[i];
        i -= i & -i;
    }
    return s;
}

void add(int i, int x)//i 不能取 0
{
    while(i<=MX){
        bit[i] += x;
        i += i&-i;
    }
}
int main()
{

    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        memset(bit,0,sizeof(bit));
        for(int i = 0;i<MAXN;i++) g[i].clear();

        scanf("%s",s);
        int slen = strlen(s);
        int len = Manacher(s,slen);
        int k = 1;
        for(int i = 2;i<len;i+=2)
        {
            p[k] = Mp[i]/2-1;
            g[k - p[k]].push_back(k);
            k++;
        }
//        for(int i = 1;i<=k;i++)
//            cout<<p[i]<<' ';
//        cout<<endl;
        MX = slen;
        ll ans = 0;
        for(int i = 1;i<=slen;i++)
        {
            for(int j = 0;j<(int)g[i].size();j++)
            {
                add(g[i][j],1);
            }
            ans += sum(min(i + p[i],slen)) - sum(i);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

M - Geometry Problem
啥话不说了,直接随机,但是n小于4的时候要特殊处理一下,因为很可能判断到三点共线的情况

#include <bits/stdc++.h>
#define ll long long
#define find fuck
#define next fuckme
using namespace std;
const int MAXN = 1e5 + 5;
const double eps = 1e-6;
struct Point
{
    double x,y;
    Point() {};
    Point(double _x,double _y)
    {
        x = _x,y = _y;
    }
    Point operator - (const Point &b) const
    {
        return Point(x - b.x,y- b.y);
    }
    double operator * (const Point &b) const
    {
        return x*b.x + y*b.y;
    }
};
Point p[MAXN];
Point get_circle(Point a,Point b,Point c)
{
    double a1 = b.x - a.x, b1 = b.y - a.y, c1 = (a1*a1 + b1*b1)/2;
    double a2 = c.x - a.x, b2 = c.y - a.y, c2 = (a2*a2 + b2*b2)/2;
    double d = a1*b2 - a2*b1;
    return Point(a.x + (c1*b2 - c2*b1)/d, a.y + (a1*c2 -a2*c1)/d);
}
double dist(Point a,Point b)
{
    return sqrt((a- b)*(a - b));
}
int main()
{

    int ca;
    scanf("%d",&ca);
    while(ca--)
    {
        int n,k;
        scanf("%d",&n);
        k = (n +1)/2;
        for(int i = 0; i<n; i++)
        {
            double x,y;
            scanf("%lf%lf",&x,&y);
            p[i] = Point(x,y);
        }
        if(n == 1)
        {
            printf("%.12f\n %.12f 0\n",p[0].x,p[0].y);
        }
        else if(n <= 4)
        {
            Point p0 = Point( (p[0].x+p[1].x)/2 , (p[0].y + p[1].y) /2);
            double r = dist(p0,p[0]);
            printf("%.12f %.12f %.12f\n",p0.x,p0.y,r);
        }
        else
        {
            int N = 1000000;
            Point p0;
            double r;
            random_device rd;
            bool f =  0;
            while(N--)
            {
                int a = 0,b = 0,c = 0;
                a = ((rd() << 15 | rd()) % n + n) % n;
                b = ((rd() << 15 | rd()) % n + n) % n;
                c = ((rd() << 15 | rd()) % n + n) % n;
                while(b == a ) b = ((rd() << 15 | rd()) % n + n) % n;
                while(c == b ||c == a) c = ((rd() << 15 | rd()) % n + n) % n;
                p0 = get_circle(p[a],p[b],p[c]);
                if(fabs(p0.x) > 1e9 || fabs(p0.y) > 1e9) continue;
                r = dist(p0,p[a]);
                int cnt = 0;
                for(int i = 0;i<n;i++)
                {
                    if(fabs(dist(p[i],p0) - r) < eps)
                    {
                        cnt++;
                        if(cnt >= k)
                        {
                            f = 1;
                            break;
                        }
                    }
                }
                if(f)
                {
                    printf("%.12f %.12f %.12f\n",p0.x,p0.y,r);
                    break;
                }
            }
            if(!f)printf("%.12f %.12f %.12f\n",p0.x,p0.y,r);
        }

    }
    return 0;
}
/*
1
4
0 1
1 0
-1 0
0 -1
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值