BestCoder 2nd Anniversary

1001 Oracle


先记录 0−90-909101010个数字分别有多少个。不难看出,最小的一个存在的数字和其余的数字降序排列的相加就是答案,但是最小的那个数字不能是000,因为题面上说明是正整数。将这两个数加起来时,注意处理进位问题。考虑无解的情况,即一串数字中仅存在111个非000数字或不存在。(PS.这道题目原本的时限是1s1s1s,考虑到题目的难度和评测机的问题,开了4s4s4s,大家可以自己在FST以后看一下时间。如果是时限是1s1s1s的话,sortsortsort是过不了的,输出也需要优化一下)

时间复杂度 O(Tn)O(Tn)O(Tn)

1002 Arrange


首先,根据题意可得B数组应是单调不升的,C数组是单调不降的。

可以发现A1=B1=C1 A_1 = B_1 = C_1 A1=B1=C1,所以如果B1≠C1 B_1 \neq C_1 B1C1无解。

进一步,我们发现如果Bi<Bi−1 B_i < B_{i-1} Bi<Bi1Ai=Bi A_i = B_i Ai=Bi;如果Ci>Ci−1 C_i > C_{i-1} Ci>Ci1Ai=Ci A_i = C_i Ai=Ci。但是如果Bi<Bi−1 B_i < B_{i-1} Bi<Bi1Ci>Ci−1 C_i > C_{i-1} Ci>Ci1同时满足,就会产生冲突导致无解。

考虑Bi=Bi−1 B_i = B_{i-1} Bi=Bi1Ci=Ci−1 C_i = C_{i-1} Ci=Ci1同时满足的情况,此时应有Ai∈(Bi,Ci) A_i \in (B_i,C_i) Ai(Bi,Ci)Ai A_i Ai没有在之前使用过。因为(Bi,Ci) (B_i,C_i) (Bi,Ci)是不断变大的,我们只需维护一下这个区间内有多少值已经被使用过了,用乘法原理统计答案即可。注意到如果某时刻Ai A_i Ai没有值可以使用,也会导致无解。

时间复杂度O(Tn) O(Tn) O(Tn)

1003 Wool


考虑三角形三条边a,b,c a,b,c a,b,c (a≥b) (a \ge b) (ab)的关系a−b<c,a+b>c a - b < c, a + b > c ab<c,a+b>c,即c∈(a−b,a+b) c \in (a-b,a+b) c(ab,a+b)

令加入的边为c c c,枚举所有边作为a a a的情况。对于所有可行的b b b,显然与a a a相差最小的可以让(a−b,a+b) (a-b,a+b) (ab,a+b)覆盖范围最大,所以可以贪心地选择不大于a a a的最大的b b b

于是我们可以先将边按长度排序,然后ai a_i aiai+1 a_{i + 1} ai+1建一条线段。线段并是不合法的部分。

将所有线段按左端点排序,按序扫描一遍,过程中统计答案即可。

时间复杂度O(Tn logn) O(Tn\ \log n) O(Tn logn)

1004 Palace


先不考虑删点。平面上n n n个点求最近点对是一个经典问题。

对所有点按照x x x坐标排序。然后分治,求出左半边和右半边的最近点对,对于两边的最近距离取较小的,记为d d d。取从分治的中间点向左右各距离为d d d的范围中的所有点,把这些点按照y y y坐标排序。对于每个点,扫一下与它y y y坐标差小于等于d d d的点,更新答案,可以证明这样的点是很少的,不超过6个。时间复杂度O(Tn log2n) O(Tn\ \log ^ 2 n) O(Tn log2n)

考虑删点的情况:

⋅\cdot 删去的点不属于最近点对,对于答案没有影响。

⋅\cdot 平面上存在两对没有公共点的最近点对,则删去的点至多属于一对最近点对,对于答案没有影响。

⋅\cdot 平面上所有最近点对有公共点,且删去的点是某个公共点,最近点对会发生变化。

只有第三种情况,删点会对答案造成影响。而只要求出最近点对,那个对答案造成影响的点一定在最近点对上。

所以只需要先对于所有点求出最近点对。然后分别删去其中的两个点,各求一遍最近点对。删去那两个点后的贡献单独计算,其余情况只需要计算全局最近点对的贡献即可。

时间复杂度O(Tn log2 n) O(Tn\ \log ^ 2\ n) O(Tn log2 n)。如果把y y y轴排序改为归并,可以做到O(Tnlogn) O(Tn \log n) O(Tnlogn)的时间复杂度。

EXTEXTEXT 删去两个点怎么做?

1005 Jewelry


枚举区间右端点r r r,考虑l l l的可行域的大小。

注意到每个字符的贡献一定是一段区间,所以l l l的可行域是若干段区间的并。

每次右端点向右移时,至多会有一个字符的区间发生改变。

所以我们需要一个支持插入一条线段,删除一条线段和查询线段并操作的数据结构。

和扫描线类似,我们可以用经典的线段树来维护。

时间复杂度O(Tn logn) O(Tn\ \log n) O(Tn logn)



1001:

#include <iostream>
#include <queue> 
#include <stack> 
#include <map> 
#include <set> 
#include <bitset> 
#include <cstdio> 
#include <algorithm> 
#include <cstring> 
#include <climits>
#include <cstdlib>
#include <cmath>
#include <time.h>
using namespace std;
typedef long long LL;

const int maxn = 10000005;

char s[maxn];
int cnt[20];

void work()
{
    memset(cnt, 0, sizeof cnt);
    scanf("%s", s);
    for(int i = 0; s[i]; i++) cnt[s[i] - '0']++;
    int flag = 0;
    for(int i = 1; i < 10; i++) flag += cnt[i];
    if(flag <= 1) {
        printf("Uncertain\n");
        return;
    }
    for(int i = 1; i < 10; i++) if(cnt[i]) {
        cnt[i]--, flag = i;
        break;
    }
    int tcnt = 0;
    for(int i = 0; i < 10; i++) tcnt += cnt[i];
    for(int i = 1, j = 9; i <= tcnt; i++) {
        while(cnt[j] == 0) j--;
        cnt[j]--;
        s[i] = j;
    }
    s[0] = 0;
    for(int i = tcnt; i >= 0; i--) {
        s[i] += flag;
        if(s[i] >= 10) s[i] -= 10, flag = 1;
        else flag = 0;
    }
    if(s[0]) {
        for(int i = 0; i <= tcnt; i++) printf("%d", s[i]);
    }
    else {
        for(int i = 1; i <= tcnt; i++) printf("%d", s[i]);
    }
    printf("\n");
}


int main()
{
    int _;
    scanf("%d", &_);
    while(_--) {
        work();
    }
    
    return 0;
}

1002


#include <iostream>
#include <queue> 
#include <stack> 
#include <map> 
#include <set> 
#include <bitset> 
#include <cstdio> 
#include <algorithm> 
#include <cstring> 
#include <climits>
#include <cstdlib>
#include <cmath>
#include <time.h>
using namespace std;
typedef long long LL;

const int maxn = 100005;

int b[maxn], c[maxn];
int vis[maxn];

void work()
{
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for(int i = 1; i <= n; i++) scanf("%d", &c[i]);
    for(int i = 2; i <= n; i++) {
        if(b[i] > b[i-1] || c[i] < c[i-1]) {
            printf("0\n");
            return;
        }
    }
    if(b[1] != c[1]) {
        printf("0\n");
        return;
    }
    memset(vis, 0, sizeof vis);
    vis[b[1]] = vis[c[1]] = 1;
    LL ans = 1, cnt;
    for(int i = 2; i <= n; i++) {
        if(vis[b[i]] && vis[c[i]]) {
            ans = ans * (c[i] - b[i] - i + 2) % 998244353;
        }
        if(!vis[b[i]] && !vis[c[i]]) ans = 0;
        vis[b[i]] = vis[c[i]] = 1;
    }
    
    printf("%lld\n", ans);
}

int main()
{
    int _;
    scanf("%d", &_);
    while(_--) {
        work();
    }
    
    return 0;
}


1003


#include <iostream>
#include <queue> 
#include <stack> 
#include <map> 
#include <set> 
#include <bitset> 
#include <cstdio> 
#include <algorithm> 
#include <cstring> 
#include <climits>
#include <cstdlib>
#include <cmath>
#include <time.h>
using namespace std;
typedef long long LL;

const int maxn = 100005;

LL a[maxn];

void work()
{
    int n;
    LL L, R;
    scanf("%d%lld%lld", &n, &L, &R);
    for(int i = 1; i <= n; i++) scanf("%lld", &a[i]);
    sort(a+1, a+n+1);
    LL ans = 0;
    for(int i = n; i >= 2; i--) {
        if(R < L) break;
        LL mx = a[i] + a[i-1] - 1;
        LL mi = a[i] - a[i-1] + 1;
        if(R > mx) ans += R - max(mx, L-1);
        R = min(R, mi-1);
    }
    if(R > L) ans += R - L + 1;
    printf("%lld\n", ans);
}

int main()
{
    int _;
    scanf("%d", &_);
    while(_--) {
        work();
    }
    
    return 0;
}


1004


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const double INF = 1e20;
const int N = 100005;
typedef long long LL;

struct Point
{
    int id;
    double x;
    double y;
}point[N], po[N];
int n, tt1, tt2;
int tmpt[N];

struct node
{
    double d;
    int id1, id2;
    node(double d = 0, int id1 = 0, int id2 = 0) : d(d), id1(id1), id2(id2) {}
    bool operator < (const node &b) const {
        return d < b.d;
    }
    bool operator > (const node &b) const {
        return d > b.d;
    }
};

bool cmpxy(const Point& a, const Point& b)
{
    if(a.x != b.x)
        return a.x < b.x;
    return a.y < b.y;
}

bool cmpy(const int& a, const int& b)
{
    return point[a].y < point[b].y;
}

node dis(int i, int j)
{
    return node(sqrt((point[i].x-point[j].x)*(point[i].x-point[j].x)
                + (point[i].y-point[j].y)*(point[i].y-point[j].y)), i, j);
}

node Closest_Pair(int left, int right)
{
    node d = node(INF, left, right);
    if(left==right)
        return d;
    if(left + 1 == right)
        return dis(left, right);
    int mid = (left+right)>>1;
    node d1 = Closest_Pair(left,mid);
    node d2 = Closest_Pair(mid+1,right);
    d = min(d1,d2);
    int i,j,k=0;
    //分离出宽度为d的区间
    for(i = left; i <= right; i++)
    {
        if(fabs(point[mid].x-point[i].x) <= d.d)
            tmpt[k++] = i;
    }
    sort(tmpt,tmpt+k,cmpy);
    //线性扫描
    for(i = 0; i < k; i++)
    {
        for(j = i+1; j < k && point[tmpt[j]].y-point[tmpt[i]].y<d.d; j++)//  理解!!
        {
            node d3 = dis(tmpt[i],tmpt[j]);
            if(d > d3)
                d = d3;
        }
    }
    return d;
}


LL solve()
{
    sort(point,point+n,cmpxy);
    node tt = Closest_Pair(0,n-1);
    double t = (tt.d);
    tt1 = point[tt.id1].id;
    tt2 = point[tt.id2].id;
    //printf("OOOO %d %d\n", tt1, tt2);
    return (LL)(t * t + 0.5);
}

void work()
{
    scanf("%d", &n);
    for(int i = 0; i < n; i++) scanf("%lf%lf", &po[i].x, &po[i].y), po[i].id = i;
    for(int i = 0; i < n; i++) point[i] = po[i];
    LL ans = solve() * (n-2);
    int t1 = tt1, t2 = tt2;
    //printf("%d %d\n", t1, t2);
    
    n = n-1;
    int cnt = 0;
    for(int i = 0; i < n + 1; i++) if(po[i].id != t1) {
        point[cnt++] = po[i];
    }
    
    ans += solve();
    
    cnt = 0;
    for(int i = 0; i < n + 1; i++) if(po[i].id != t2) {
        point[cnt++] = po[i];
    }
    
    ans += solve();
    printf("%lld\n", ans);
}

int main()
{
    int _;
    scanf("%d", &_);
    while(_--) work();
    
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值