2017哈尔滨ccpc部分题解

A题思路:最主要的点就是要得出这个结论: 设p[i]为以i为中心的回文半径-1,那么如果满足j-i<=min(p[i],p[j]),那么j就可以和i组成究极回文串。
所以由这个式子可以看出三点:
1,i < j
2,j-i < p[i]
3,j-i < p[j]
由第三个可以得出i>j+p[j],所以算可以和i组成究极回文串的时候得将所有满足j+p[j]的j算进去,再计算满足小于i+p[i]的j的个数。这里可以用bit

#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int maxn = 500005+10;
char str[maxn];
char tmp[maxn << 1];
int Len[maxn << 1];
int  INIT()
{
    int i, len = strlen(str);
    tmp[0] = '@';
    for (i = 1;i <= len;i ++)
    {
        tmp[i] = str[i - 1];
    }
    tmp[len + 1] = '#';
    tmp[ len + 2] = '$';
    tmp[len + 3] = '\0';
    return len + 1;
}
void MANACHER(int len)
{
    int mx = 0, ans = 0, po = 0;
    for (int i = 1;i <= len;i++)
    {
        if (mx > i)
            Len[i] = min(mx - i, Len[2 * po - i]);
        else
            Len[i] = 1;
        while (tmp[i - Len[i]] == tmp[i + Len[i]])
            Len[i]++;
        if (Len[i] + i > mx)
        {
            mx = Len[i] + i;
            po = i;
        }
    }
}
vector<int>V[maxn];
#define lowbit(x) x&-x
int sum[maxn];
void add(int x, int num)
{
    while (x<maxn)
    {
        sum[x] += num;
        x += lowbit(x);
    }
}
int query(int x)
{
    int ans = 0;
    while (x)
    {
        ans += sum[x];
        x -= lowbit(x);
    }
    return ans;
}
int p[maxn];
int str_len;
void init()
{
    memset(sum, 0, sizeof(sum));
    for (int i = 0;i < str_len+5;i++)V[i].clear();
    memset(Len, 0, sizeof(Len));
}

int main()
{
    int T;
    cin >> T;
    while (T--)
    {
        scanf("%s", str);
         str_len = strlen(str);
        init();
        int changelen=INIT();
        MANACHER(changelen);
        for (int i = 1;i < changelen;i ++)
        {
            int pp = Len[i] - 1;
            V[i - pp].push_back(i);
            p[i] = pp;
        }
        long long ans = 0;
        for (int i = 1;i <= str_len;i++)
        {
            for (int j = 0;j < V[i].size();j++)
            {
                add(min(str_len,V[i][j]), 1);
            }
            ans += query(min(i + p[i],str_len)) - query(i);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

B题题意:求一个序列的所有子序列中第k大的数组成的序列中第M大的数。
思路:对于一个数来说,它是否是组成的序列中的第M大可以这样判断:在原序列的所有子序列中的第k大的数中,大于该数字的个数超过了M,那么答案肯定是比该数大的,否则比答案小。所以这里采用二分,而求个数可以采用尺取法。
代码简短可以自行推理。

#include<iostream>
using namespace std;

const int maxn = 100005;
int a[maxn];
typedef long long ll;
int N, K;
ll M;
bool check(int num)
{
    int j = 1;
    int tot = 0;
    ll ans = 0;
    for (int i = 1;i <= N;i++)
    {
        if (a[i] >= num)
            tot++;
        if (tot == K)
        {
            ans += N - i + 1;
            while (a[j] < num)
            {
                ans += N - i + 1;
                j++;
            }
            tot--;
            j++;
        }
    }
    if (ans >= M)return 1;
    return 0;
}

int main()
{
    int T;
    scanf("%d", &T);
    while (T--)
    {

        scanf("%d %d %lld", &N, &K, &M);
        for (int i = 1;i <= N;i++)
            scanf("%d", &a[i]);
        int l = 1, r = 1e9 + 5;
        int mid;
        while (l<=r)
        {
            mid = l + r >> 1;
            if (check(mid))
                l = mid+1;
            else
                r = mid - 1;
        }

        printf("%d\n", r);
    }
    return 0;


}

M题:思路:随机化+计算几何,不是很难,主要是第一次写随机化。

#include<iostream>
#include<time.h>
#include<algorithm>
#include<ostream>
#include<math.h>
using namespace std;
#define eps 1e-8
#define fabs(x) ((x)>0?(x):-(x))
#define zero(x) (fabs(x)<eps)
#define _sign(x) ((x)>eps?1:((x)<-eps?2:0))
#define sqr(x) ((x)*(x))
#define MAXN 100005
const double pi = acos(-1);
struct point
{
    int idx;
    double ang;
    double x, y;
    point() { x = 0;y = 0; }
    point(double sx, double sy)
    {
        x = sx;y = sy;
    }
    void read()
    {
        scanf("%lf %lf", &x, &y);
    }
};

struct line
{
    double ang;
    point a, b;line() {};
    line(const point &p1, const point &p2)
    {
        a = p1;b = p2;
    }
};

typedef const point CP;
typedef const line CL;
point P[MAXN];
int N;

struct make
{
    double dis(CP &p1, CP &p2)
    {
        return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
    }
    point intersection(CL &u, CL &v)
    {
        point ret = u.a;
        double t = ((u.a.x - v.a.x)*(v.a.y - v.b.y) - (u.a.y - v.a.y)*(v.a.x - v.b.x)) / ((u.a.x - u.b.x)*(v.a.y - v.b.y) - (u.a.y - u.b.y)*(v.a.x - v.b.x));
        ret.x += (u.b.x - u.a.x)*t;
        ret.y += (u.b.y - u.a.y)*t;
        return ret;
    }
    point circumcenter(CP &a, CP &b, CP &c)
    {
        line u, v;
        u.a.x = (a.x + b.x) / 2;
        u.a.y = (a.y + b.y) / 2;
        u.b.x = u.a.x - a.y + b.y;
        u.b.y = u.a.y + a.x - b.x;
        v.a.x = (a.x + c.x) / 2;
        v.a.y = (a.y + c.y) / 2;
        v.b.x = v.a.x - a.y + c.y;
        v.b.y = v.a.y + a.x - c.x;
        return intersection(u, v);
    }
}Making;

bool check(point u, double r)
{
    int tot = 0;
    for (int i = 1;i <= N;i++)
    {
        if (zero(Making.dis(P[i], u) - r))
            tot++;
    }
    if (tot >= (N - 1) / 2 + 1)
        return 1;
    return 0;
}
void solve()
{
    if (N == 1)
        printf("%lf %lf 0\n", P[1].x, P[1].y);
    else if (N <= 4)
    {
        point temp = { (P[1].x + P[2].x) / 2, (P[1].y + P[2].y) / 2 };
        double r = Making.dis(P[1], P[2]) / 2;
        printf("%lf %lf %lf\n", temp.x, temp.y, r);
    }
    else
    {
        while (1)
        {
            int a = ((rand() << 15 | rand()) % N + N) % N + 1;
            int b = ((rand() << 15 | rand()) % N + N) % N + 1;
            int c = ((rand() << 15 | rand()) % N + N) % N + 1;
            while (a == b)
                b = ((rand() << 15 | rand()) % N + N) % N + 1;
            while (c == a || c == b)
                c = ((rand() << 15 | rand()) % N + N) % N + 1;
            point d = Making.circumcenter(P[a], P[b], P[c]);
            double r = Making.dis(d, P[a]);
            if (check(d, r))
            {
                //cout << a << endl;
                printf("%lf %lf %lf\n", d.x, d.y, r);
                break;
            }
        }
    }
}

int main()
{
    srand(time(NULL));
    int T;
    scanf("%d", &T);
    while (T--)
    {

        scanf("%d", &N);
        for (int i = 1;i <= N;i++)
            P[i].read();
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值