Codeforces Good Bye 2014 부분 문제 풀이

12014 단어 codeforces2014Goodbye
저는 문제를 제시하지 않겠습니다. 여러분들이 흥미가 있으면 직접 가서 보셔도 됩니다...어쨌든 나는 게으르다...
Solution A
이 문제는 바로 제목이 준 조건에 따라 풀면 된다.
아흥고로 디프엑스 만들 수 있어요.
위치가 단조롭기 때문에,
그러니까 우리가 직접 한 번 훑어보면 돼.
시간의 복잡도는 O(n)이다.
Code A
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 300000 + 5

int n, k, Next[N];

int main()
{
    scanf("%d%d", &n, &k);
    int u = 1;
    bool ok = 0;
    for (int i = 1; !ok && i < n; i ++)
    {
        scanf("%d", Next + i);
        Next[i] += i;
        u = u == i ? Next[i] : u;
        if (u == k) ok = 1;
    }
    puts(ok ? "YES" : "NO");
    return 0;
}

Solution B라는 문제는 A[i][j]==1의 문제만 바꿀 수 있는 것으로 보입니다.
만약 우리가 A[][]를 인접 행렬로 간주한다면,
실제로 u와 v가 같은 연결 블록 안에 있으면
u는 v와 직접 교환할 수 있습니다.
【증명】
u에서 v까지의 경로가 있다고 가정하면 u-a-...b - v
그러면 우리는 u로 v로 바꿔서 이렇게 된다.
a - ... - b - v - u
그리고 v로 a로 바꿔서 이렇게 돼요.
v - a - ... - b - u.
이것은 u와 v의 직접 교환과 같다.
좋습니다. 사전의 순서가 가장 작은 배열을 요구하기 때문에,
그러면 우리는 모든 연결 블록 안의 원소에 순서를 배열할 수 있다.
사전의 순서가 가장 작은 배열을 얻을 수 있다.
시간의 복잡도는 O(n^2)이다.
Code B
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 300 + 5

int n, A[N], Fa[N], Ord[N];
bool Map[N][N], Flag[N];

inline int Find(int x)
{
    return x == Fa[x] ? x : Fa[x] = Find(Fa[x]);
}

inline bool cmp(int u, int v)
{
    return A[u] < A[v];
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
    {
        scanf("%d", A + i);
        Ord[i] = Fa[i] = i;
    }
    sort(Ord + 1, Ord + n + 1, cmp);
    char ch = '
'; for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) { while (ch != '0' && ch != '1') ch = getchar(); if (ch == '1') Fa[Find(i)] = Find(j); ch = getchar(); } for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) Map[i][j] = Find(i) == Find(j); for (int i = 1; i <= n; i ++) for (int j = 1; j <= n; j ++) if (Map[i][Ord[j]] && !Flag[j]) { printf("%d%c", j, i == n ? '
' : ' '); Flag[j] = 1; break ; } return 0; }

Solution C
이 문제는 보기에 그리 쉽지 않은 것 같다.
우선 이 문제는 책의 배치 순서를 찾아야 한다.
제목에 따라 책을 읽을 때 총 무게를 최소화할 수 있다.
우리는 먼저 이렇게 해서 순서를 확정할 수 있다.
순서 매거 독서 순서표,
만약 현재의 책을 아직 보지 못했다면,
그럼 지금 다 본 책 밑에 놓고
안 그러면 몰라.
처음부터 끝까지 보지 못한 책에 대해
임의의 순서대로 맨 아래에 놓아라.
이런 순서가 가장 좋다.
【위증】
우선 책을 본 적이 있다면,
그러면 이 책 뒤의 위치가 확정된다.
그래서 우리는 어느 순간에 한 번도 보지 못한 책을 생각하고 있다.
만약 우리가 이 책을 아래에 놓는 것이 위에 놓는 것보다 낫다는 것을 증명할 수 있다면,
그러면 우리는 모든 책이 조건을 충족시키는 방안을 세울 수 있고,
그래서 이 문제는 해결되었다.
만약 이 책이 이전에 읽을 책 위에 놓여 있다면,
그럼 책을 가져올 때,
우리가 고려한 이 책은 답안에 기여해야 한다.
밑에 놓으면,
공헌이 되지 않는다.
그래서 앞서 말한 순서 구조 방법이 가장 좋다.
(증명이 위선적인 것 같아...)
이제 우리는 이런 책의 배치 순서를 얻었고,
다음은 바로 시뮬레이션입니다.
책 한 권을 보는데 필요한 책의 수량은 O(n)이다.
m권의 책을 가져가려면,
그러니까 우리가 직접 폭력 시뮬레이션을 하면 돼.
어쨌든 데이터 범위는 좁다.
시간 복잡도 O(nm).
Code C
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1500 + 5

int n, m, ans, A[N], B[N], Ord[N];
bool Flag[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++)
        scanf("%d", A + i);
    for (int i = 1; i <= m; i ++)
    {
        scanf("%d", B + i);
        if (!Flag[B[i]]) Ord[++ Ord[0]] = B[i];
        Flag[B[i]] = 1;
    }
    for (int i = 1; i <= m; i ++)
    {
        int j = 1;
        for (; Ord[j] != B[i]; j ++) ans += A[Ord[j]];
        for (; j > 1; j --) swap(Ord[j], Ord[j - 1]);
    }
    printf("%d
", ans); return 0; }

Solution D
이 문제는 그래도 비교적 예쁘다.
체인의 가치에 대한 기대를 우리는 유지하기 어렵다.
O(n^2) 체인이 있기 때문에 수정도 해야 합니다...
어차피 난 못해.
그래서 우리는 모든 측면에서 답안에 대한 공헌을 고려할 수 있다.
우선 우리는 기대는 추가할 수 있다는 것을 안다.
그래서 우리가 이렇게 하는 것은 괜찮다.
우리는 얼마나 많은 상황에서 이 변이 나타날지 고려할 수 있다.
x를 이 변의 한쪽 끝의 점의 개수로 설정하고 다른 한쪽 끝에는 (n-x)의 점이 있다.
따라서 x*(x-1)/2*(n-x)+(n-x)*(n-x-1)/2*x의 경우
이 쪽은 답안에 기여할 것이다.
그리고 나서 우리는 한 변이 계산되지 않거나 두 번 계산되는 것을 알아차렸다.
그래서 한 변의 기대에 대한 공헌은 W * [x * (x - 1) * (n - x) + (n - x) * (n - x - 1) * x]/[n * (n - 1) * (n - 2)]
여기서 W는 모서리의 모서리입니다.
그래서 문제 전체가 가볍고 유쾌해졌다.
질문을 O(1)처리할 수 있습니다.
시간 복잡도 O(n+q).
Code D
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
typedef long double LD;
#define N 100000 + 5

int n, tot = 1, Head[N], Size[N], E[N], W[N];
LD ans;
LL A_n_3;

struct Edge
{
    int next, node, w;
}h[N << 1];

inline void addedge(int u, int v, int w)
{
    h[++ tot].next = Head[u], Head[u] = tot;
    h[tot].node = v, h[tot].w = w;
    h[++ tot].next = Head[v], Head[v] = tot;
    h[tot].node = u, h[tot].w = w;
}

inline void dfs(int z, int fa)
{
    Size[z] = 1;
    for (int i = Head[z]; i; i = h[i].next)
    {
        int d = h[i].node;
        if (d == fa) continue ;
        dfs(d, z);
        Size[z] += Size[d];
        W[i >> 1] = Size[d];
    }
}

inline LD Calc(int pos, int delta)
{
    LL t_1 = (LL) W[pos] * (W[pos] - 1) * (n - W[pos]) * 6;
    LL t_2 = (LL) W[pos] * (n - W[pos]) * (n - W[pos] - 1) * 6;
    LD t = (LD) (t_1 + t_2) / A_n_3;
    return t * delta;
}

int main()
{
    scanf("%d", &n);
    A_n_3 = (LL) n * (n - 1) * (n - 2);
    for (int i = 1; i < n; i ++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addedge(u, v, w);
        E[i] = w;
    }
    dfs(1, 0);
    for (int i = 1; i < n; i ++)
        ans += Calc(i, E[i]);
    int q;
    scanf("%d", &q);
    while (q --)
    {
        int pos, num;
        scanf("%d%d", &pos, &num);
        ans -= Calc(pos, E[pos]);
        ans += Calc(pos, num);
        E[pos] = num;
        printf("%.7lf
", (double) ans); } return 0; }

Solution E
만약 우리가 i를 일일이 열거하고 왼쪽 단점을 1로 유지할 수 있다면...i, 오른쪽 단점 i의 답안.
그래서 이 문제는 오프라인으로 풀 수 있다.
사실도 마찬가지다.
우리는 그 하나를 하나의 선으로 바꿀 수 있다 [L, L + H].
그래서 문제는 구(l,r)의 선의 공백 부분 크기로 바뀌었다.
공백은 앞의 것이 무엇인지 뒤의 것이 닿지 않는다는 것을 의미한다.
그래서 우리는 라인의 오른쪽 단점 위치에 따라 단조로운 체감 창고를 유지할 수 있다.
이것은 창고에 있는 두 인접 원소 사이의 모든 것을 통일적으로 고려할 수 있다는 것을 의미한다.
그리고 두 인접 원소 사이의 모든 것을 통일적으로 처리할 수 있다.
우리는 다음과 같은 점을 알아차렸다.
만약 어떤 결정 구간의 그 무엇이 현재의 매거에 미치지 못한다면,
그러면 우리는 이 구간의 답안에 대해 구간 증량을 진행할 것이다.
그리고 이 구간은 삭제될 것이다.
그래서:
그 무엇이 대표하는 구간마다 O(1)회 조작만 할 수 있다.
그래서 이 문제는 구간 증량, 단일 문의,
시간의 복잡도는 O(n log n)이다.
아...내가 분명히 말하지 못한 것 같아...
그럼 제 코드를 보세요...TAT
Code E
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
#define N 200000 + 5
#define M 600000 + 5
#define ls(x) x << 1
#define rs(x) x << 1 | 1

int n, T, size, L[N], R[N], q[N];
LL Ans[N];

struct Querys
{
    int l, r, id;
    Querys (int _l = 0, int _r = 0, int _id = 0) {l = _l, r = _r, id = _id;}
    bool operator < (const Querys a) const
    {
        return r < a.r;
    }
}Ask[N];

struct Segment_Tree
{
    LL num, delta;
}h[M];

inline void apply(int x, int d)
{
    h[x].num += d;
    h[x].delta += d;
}

inline void push(int x)
{
    if (h[x].delta)
    {
        apply(ls(x), h[x].delta);
        apply(rs(x), h[x].delta);
        h[x].delta = 0;
    }
}

inline void Modify(int x, int l, int r, int s, int t, int d)
{
    if (l == s && r == t)
    {
        apply(x, d);
        return ;
    }
    push(x);
    int mid = l + r >> 1;
    if (t <= mid)
        Modify(ls(x), l, mid, s, t, d);
    else if (s > mid)
        Modify(rs(x), mid + 1, r, s, t, d);
    else Modify(ls(x), l, mid, s, mid, d), Modify(rs(x), mid + 1, r, mid + 1, t, d);
}

inline LL Query(int x, int l, int r, int t)
{
    if (l == r)
        return h[x].num;
    push(x);
    int mid = l + r >> 1;
    if (t <= mid)
        return Query(ls(x), l, mid, t);
    else return Query(rs(x), mid + 1, r, t);
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i ++)
    {
        scanf("%d%d", L + i, R + i);
        R[i] += L[i];
    }
    scanf("%d", &T);
    for (int i = 1; i <= T; i ++)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        Ask[i] = Querys(l, r, i);
    }
    sort(Ask + 1, Ask + T + 1);
    
    int t = 1;
    for (int i = 1; i <= n; i ++)
    {
        for (; R[q[size]] <= R[i] && size; size --)
            if (R[q[size]] < L[i])
                Modify(1, 1, n, q[size - 1] + 1, q[size], L[i] - R[q[size]]);
        q[++ size] = i;
        while (Ask[t].r == i && t <= T)
        {
            Ans[Ask[t].id] = Query(1, 1, n, Ask[t].l);
            t ++;
        }
    }
    for (int i = 1; i <= T; i ++)
        printf("%I64d
", Ans[i]); return 0; }

Solution F
이 문제는 틀림없이 분치일 것이다.
우리는 Solve(l,r)를 만들 수 있는데,
처리 시간이 [l,r]에 있는 문의를 나타낸다.
우선, 어떤 item이 [l, r] 이 모든 시간 구간에서 살 수 있다면,
그럼 이 아이템 Dp를 한번,
01배낭이에요.
그리고 이 물건에'이미 처리되었다'는 표시를 하세요.
그런 다음 Solve(l,mid)와 Solve(mid + 1,r)를 반복합니다.
l==r가 될 때까지 문의 시간을 기록하면 딱 l의 답이다.
반드시 거슬러 올라가야 한다!
그리고 가장 중요한 점:
하나의 아이템은 O(log n)회 처리됩니다.
모든 세그먼트가 세그먼트 트리의 O(log n) 구간에 대응하는 것과 유사합니다.
그래서 시간의 복잡도는 O(nT + nB log n)
T는 시간의 최대치이고 B는 예산 금액의 최대치이다.
Code F
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 4000 + 5
#define M 20000 + 5
#define INF 0x7fffffff

int n, p, q, Max, _Max, C[N], W[N], A[N], Dp[21][N], Max_Dp[21][N], T[M], B[M], Ans[M], Done[N];
vector <int> Map[M];

inline void Solve(int depth, int l, int r)
{
	for (int i = 1; i <= n; i ++)
		if (!Done[i] && A[i] <= l && A[i] + p > r)
		{
			Done[i] = depth;
			for (int j = _Max; j >= C[i]; j --)
				Dp[depth][j] = max(Dp[depth][j], Dp[depth][j - C[i]] + W[i]);
		}
	for (int i = 0; i <= _Max; i ++)
		Max_Dp[depth][i] = i == 0 ? Dp[depth][i] : max(Dp[depth][i], Max_Dp[depth][i - 1]);
	if (l == r)
	{
		for (int i = 0; i < Map[l].size(); i ++)
			Ans[Map[l][i]] = Max_Dp[depth][B[Map[l][i]]];
		for (int i = 1; i <= n; i ++)
			if (Done[i] == depth) Done[i] = 0;
		return ;
	}
	int mid = l + r >> 1;
	for (int i = 0; i <= _Max; i ++)
		Dp[depth + 1][i] = Dp[depth][i];
	Solve(depth + 1, l, mid);
	for (int i = 0; i <= _Max; i ++)
		Dp[depth + 1][i] = Dp[depth][i];
	Solve(depth + 1, mid + 1, r);
	for (int i = 1; i <= n; i ++)
		if (Done[i] == depth) Done[i] = 0;
}

int main()
{
	scanf("%d%d", &n, &p);
	for (int i = 1; i <= n; i ++)
		scanf("%d%d%d", C + i, W + i, A + i);
	scanf("%d", &q);
	for (int i = 1; i <= q; i ++)
	{
		scanf("%d%d", T + i, B + i);
		Max = max(Max, T[i]);
		_Max = max(_Max, B[i]);
		Map[T[i]].push_back(i);
	}
	for (int i = 1; i < _Max; i ++)
		Dp[0][i] = -INF;
	Solve(0, 1, Max);
	for (int i = 1; i <= q; i ++)
		printf("%d
", Ans[i]); return 0; }

G문제에 관해서 나는 너무 약해서 할 줄 모른다.그러니 내가 만났을 때 다시 갱신하자.
그래서 이게 부분적인 문제라고 할 수 밖에 없어요...TAT...=_=

좋은 웹페이지 즐겨찾기