2020ICPC昆明【个人题解HIJLM】

目录

  • H - Hard Calculation(签到)
    • 思路
  • I - Mr. Main and Windmills(计算几何、暴力)
    • 思路
    • 代码
  • J - Parallel Sort(思维)
    • 思路
    • 代码
  • L - Simone and graph coloring(思维、dp)
    • 思路
    • 代码
  • M - Stone Games(思维、可持久化线段树)
    • 思路
    • 代码

H - Hard Calculation(签到)

思路

直接输出2020+x即可。

I - Mr. Main and Windmills(计算几何、暴力)

思路

对于第 i i i个点,它会与第 j j j个点 ( j ≠ i ) (j \ne i) (j=i)交换位置当且仅当两点连线的与线段 s t st st有交点。
所以对于每个询问,求出指定点与其他点与线段 s t st st的交点即可(如果有的话),如果交点个数小于 k i k_i ki,那么输出-1;
否则,按照每个交点与点 s s s的距离升序排序之后,输出第 k i k_i ki个交点的坐标即可。
一些小细节:
判断交点是否在线段 s t st st上,可以直接判断交点的横坐标是否在 s s s t t t中间
注意特殊处理垂直的线段

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1005;

int n, m;
int xs, ys, xt, yt;
int x[N], y[N];

struct Node
{
    double x, y;
    double s;
    bool operator < (const Node& b) const
    {
        return s < b.s;
    }
}a[N];

double getk(double x1, double y1, double x2, double y2)
{
    if (x1 == x2) return -1;
    return 1.0 * (y1 - y2) / (x1 - x2);
}

double getb(double x, double y, double k)
{
    return y - k * x;
}

double getdis(double x1, double y1, double x2, double y2)
{
    double dx = x1 - x2, dy = y1 - y2;
    return dx * dx + dy * dy;
}

void getPoint(double& X, double& Y, double k1, double b1, double k2, double b2)
{
    if (k1 == k2){
        X = Y = 1e18;
        return;
    }
    if (k1 == -1.0)
    {
        X = b1;
        Y = k2 * X + b2;
    }
    else if (k2 == -1.0)
    {
        X = b2;
        Y = k1 * X + b1;
    }
    else 
    {
        X = (b2 - b1) / (k1 - k2);
        Y = k1 * X + b1;
    }
}

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    
    scanf("%d%d", &n, &m);
    scanf("%d%d%d%d", &xs, &ys, &xt, &yt);
    for (int i = 1; i <= n; i ++ )
        scanf("%d%d", &x[i], &y[i]);

    double Kst = getk(xs, ys, xt, yt);
    double Bst;
    if (Kst != -1.0)
        Bst = getb(xs, ys, Kst);
    else Bst = xs;

    

    for (int i = 1; i <= m; i ++ )
    {
        int hi, ki;
        scanf("%d%d", &hi, &ki);
        int xi = x[hi], yi = y[hi];

        int cnt = 0;
        for (int j = 1; j <= n; j ++ )
            if (j != hi)
            {
                double K = getk(xi, yi, x[j], y[j]);
                double B;
                if (K != -1.0) B = getb(xi, yi, K);
                else B = xi;

                // printf("xi = %d yi = %d x[j] = %d y[j] = %d K = %.2f B = %.2f ", xi, yi, x[j], y[j], K, B);
                
                double X, Y;
                getPoint(X, Y, K, B, Kst, Bst);
                // printf("X = %.2f Y = %.2f\n", X, Y);
                if (min(xs, xt) <= X && X <= max(xs, xt)) 
                    a[++ cnt] = {X, Y, getdis(X, Y, xs, ys)};
            }
        if (cnt < ki)
            puts("-1");
        else 
        {
            sort(a + 1, a + 1 + cnt);
            printf("%.10f %.10f\n", a[ki].x, a[ki].y);
        }
    }

    return 0;
}

J - Parallel Sort(思维)

思路

分为下述三种情况:
1、如果排列已经有序,那么输出0
2、否则,如果对于每一个位置都有 i = = p [ p [ i ] ] i==p[p[i]] i==p[p[i]]的话,则一轮交换即可有序,即这一轮中的每一次交换都将无序的位置 i i i与位置 p [ i ] p[i] p[i]交换,不难证明这样交换不会出现重复的交换下标(因为无序的交换完就有序了,那么不会再被用去交换)
3、否则(存在 i ! = p [ p [ i ] ] i != p[p[i]] i!=p[p[i]]的位置),可以证明只需要两轮交换可以使原序列有序。第一轮交换一定可以把所有位置变为 i = = p [ p [ i ] ] i==p[p[i]] i==p[p[i]],第二轮交换同第二种情况。
那么如何在第一轮交换中把所有位置变为 i = = p [ p [ i ] ] i == p[p[i]] i==p[p[i]]。对于每一个 i ! = p [ p [ i ] ] i != p[p[i]] i!=p[p[i]]的位置,我们找到数字 i i i在原序列中的位置 p o s [ i ] pos[i] pos[i],交换位置 p o s [ i ] pos[i] pos[i]位置 p [ i ] p[i] p[i]的数,此时位置 i i i已经满足 i = = p [ p [ i ] ] i == p[p[i]] i==p[p[i]],此时位置 p o s [ i ] pos[i] pos[i]不一定满足条件,于是需要继续往下考察,直到满足条件为止。(好难说清楚啊 自己手玩一下吧)
可以证明第一轮交换也是不会有下标冲突滴。

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 100005;

int n;
int p[N];
int pos[N];

bool check0()
{
    for (int i = 1; i <= n; i ++ )
        if (p[i] != i)
            return false;
    return true;
}

bool check1()
{
    for (int i = 1; i <= n; i ++ )
        if (p[p[i]] != i)
            return false;
    printf("1\n");
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
        if (p[i] != i)
            cnt ++;
    printf("%d", cnt / 2);
    for (int i = 1; i <= n; i ++ )
        if (p[i] != i && i < p[i])
            printf(" %d %d", i, p[i]);
    puts("");
    return true;
}

void solve()
{
    printf("2\n");
    vector<PII> ans;
    for (int i = 1; i <= n; i ++ )
        if (p[p[i]] != i)
        {
            int j = i;
            while (p[p[j]] != j)
            {
                swap(p[pos[j]], p[p[j]]);
                ans.push_back(make_pair(pos[j], p[j]));
                j = pos[j];
            }
        }

    printf("%d", (int)ans.size());
    int sz = ans.size();
    for (int i = 0; i < sz; i ++ )
        printf(" %d %d", ans[i].first, ans[i].second);
    puts("");
    int cnt = 0;
    for (int i = 1; i <= n; i ++ )
        if (p[i] != i)
            cnt ++;
    printf("%d", cnt / 2);
    for (int i = 1; i <= n; i ++ )
        if (p[i] != i && i < p[i])
            printf(" %d %d", i, p[i]);
    puts("");
}

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif

    scanf("%d", &n);
    for (int i = 1; i <= n; i ++ )
        scanf("%d", &p[i]), pos[p[i]] = i;
    
    if (check0())
    {
        puts("0");
        return 0;
    }

    if (check1())
        return 0;

    solve();
    
    return 0;
}

L - Simone and graph coloring(思维、dp)

思路

通过观察可以发现,一段下降子序列中,每两个点之间都有连边,那么这段子序列中每两个点的颜色都不能一样,于是所需要的总颜色数为最长下降子序列的长度。把每个点的颜色涂成以他结尾的最长下降子序列的长度即可。
最长下降子序列可以用二分或者树状数组求。

代码

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;

const int N = 1000005;

int n;
int a[N];
int v[N];
int ans[N];

int main()
{
    #ifdef ZYCMH
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    #endif
    int _; scanf("%d", &_);
    while (_ --)
    {
        scanf("%d", &n);
        for (int i = 1; i <= n; i ++ )
            scanf("%d", &a[i]), v[i] = 0;
        
        v[0] = n + 1;
        for (int i = 1; i <= n; i ++ )
        {
            int l = 0, r = n;
            while (l < r)
            {
                int mid = (l + r + 1) / 2;
                if (v[mid] > a[i])
                    l = mid;
                else r = mid - 1;
            }
            ans[i] = l + 1;
            v[l + 1] = max(v[l + 1], a[i]);
        }

        int res = 0;
        for (int i = 1; i <= n; i ++ )
            res = max(res, ans[i]);
        printf("%d\n", res);
        for (int i = 1; i <= n; i ++ )
        {
            if (i > 1) printf(" ");
            printf("%d", ans[i]);
        }
        puts("");
    }
    return 0;
}

M - Stone Games(思维、可持久化线段树)

思路

代码

#include 

using namespace std;

typedef long long LL;

const int N = 1000005, INF = 1e9;

int n, q;
int a[N];
int rt[N];
struct Tree
{
    int l, r;
    LL sum;
}t[N * 50];
int cnt;

int update(int i, int l, int r, int pos)
{
    int p = ++ cnt;
    t[p] = t[i];
    t[p].sum += pos;
    if (l == r) 
        return p;
    int mid = (l + r) >> 1;
    if (pos <= mid) t[p].l = update(t[i].l, l, mid, pos);
    else t[p].r = update(t[i].r, mid + 1, r, pos);
    return p;
}   

LL query(int L_rt, int R_rt, int l, int r, int L, int R)
{
    if (L <= l && r <= R) return t[R_rt].sum - t[L_rt].sum;
    int mid = (l + r) >> 1;
    LL s = 0;
    if (L <= mid) s += query(t[L_rt].l, t[R_rt].l, l, mid, L, R);
    if (R > mid) s += query(t[L_rt].r, t[R_rt].r, mid + 1, r, L, R);
    return s; 
}

int main()
{

    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i ++ )
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i ++ )
        rt[i] = update(rt[i - 1], 1, INF, a[i]);

    LL last = 0;
    for (int i = 1; i <= q; i ++ )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        l = (l + last) % n + 1;
        r = (r + last) % n + 1;
        if (l > r) swap(l, r);
        LL x = 0;
        while (true)
        {
            LL t = query(rt[l - 1], rt[r], 1, INF, 1, min(1LL * INF, x + 1));
            if (t == x) break;
            x = t;
        }
        printf("%lld\n", x + 1);
        last = x + 1;
    }

    return 0;
}

你可能感兴趣的:(XCPC题解,算法)