A.Alarm Clock

题意:

一个人要睡分钟,分钟后闹钟响。如果响的时候没睡够分钟,会再设分钟后响,并花分钟重新入睡,如果还没睡够则重复上述操作。判断能否睡够分钟,如果能,输出起床时间

题解:

如果,则睡分钟即可,如果,则一定不能睡够,其余情况都能睡够,其时间为

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
void solve()
{
    int a, b, c, d;
    scanf("%d%d%d%d", &a, &b, &c, &d);
    if (b >= a)
    {
        printf("%d\n", b);
        return;
    }
    else if (d >= c)
    {
        puts("-1");
        return;
    }
    printf("%lld\n", b + (a - b + (c - d - 1ll)) / (c - d) * c);
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

B.Ternary String

题意:

给一个字符串,找到一个子串,要求满足该字串包含并且是所有满足条件中最短的,输出其长度

题解:

遍历序列,更新最近一次的位置,包含该位置最短的字串长度就是该位置的下标减去最新一次最小的那个下标的值,取即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
int pre[3];
void solve()
{
    int len = INF;
    string s;
    cin >> s;
    memset(pre, -1, sizeof(pre));
    for (int i = 0; i < s.length(); i++)
    {
        pre[s[i] - '1'] = i;
        int x = *min_element(pre, pre + 3);
        if (x != -1)
            len = min(len, i - x + 1);
    }
    if (len != INF)
        printf("%d\n", len);
    else
        puts("0");
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

C1.Simple Polygon Embedding

题意:

给定一个边长为的正边形,要求能完全将它嵌入的正方形的边长,其中为偶数

题解:

图片说明
以正八边形为例,可以看出即为所求,,其中
因此

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
void solve()
{
    int n;
    scanf("%d", &n);
    printf("%.7f\n", 1 / tan(asin(1) / n));
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

C2.Not So Simple Polygon Embedding

题意:

给定一个边长为的正边形,要求能完全将它嵌入的正方形的边长,其中为奇数

题解:

图片说明
以六边形为例,作的垂直平分线,易证得
可以求出AB长度为
作垂线,即为所求,,其中可以发现,求导可得当时最大,于是

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int maxn = 2e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double pi = acos(-1);
void solve()
{
    int n;
    scanf("%d", &n);
    double a = pi / (n * 2);
    double r = 1 / sin(a);
    a /= 2;
    printf("%.9lf\n", r * cos(a));
}
int main()
{
    int t;
    for (scanf("%d", &t); t >= 1; t--)
        solve();
}

D.Multiset

题意:

给定一个长度为的数组,有次询问,每次询问给出一个,如果,则把插入数组中,如果,则删除数组中第小的数。最后如果数组中还有数,则任意输出其中一个,否则输出

题解:

用树状数组来存数组中的所有数,每加入一个数就把它加入到树状数组中。删掉第小的数,先用二分在树状数组中找到该值,再从树状数组中删去即可。最终遍历数组判断此时树状数组单点的值是否大于,是则输出,否则输出

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 5;
const int INF = 0x3f3f3f3f;
int c[maxn];
inline int lowbit(int x) { return x & (-x); }
void add(int x, int val)
{
    for (; x < maxn; x += lowbit(x))
        c[x] += val;
}
int query(int x)
{
    int res = 0;
    for (; x; x -= lowbit(x))
        res += c[x];
    return res;
}
int main()
{
    int n, q;
    scanf("%d%d", &n, &q);
    for (int i = 0, x; i < n; i++)
    {
        scanf("%d", &x);
        add(x, 1);
    }
    while (q--)
    {
        int k;
        scanf("%d", &k);
        if (k >= 0)
            add(k, 1);
        else
        {
            k = -k;
            int l = 1, r = maxn;
            while (l < r)
            {
                int mid = (l + r) >> 1;
                if (query(mid) >= k)
                    r = mid;
                else
                    l = mid + 1;
            }
            add(l, -1);
        }
    }
    if (query(maxn - 1) == 0)
        puts("0");
    else
        for (int i = 1; i < maxn; i++)
            if (query(i) - query(i - 1) > 0)
            {
                cout << i << '\n';
                break;
            }
    return 0;
}

E.Graph Coloring

题意:

给定个点条边的无向图,不保证联通,给每个点标号号点个数分别为。要求每条边的两点,标号之差绝对值为
如果存在一种满足条件方案,则输出标点的方案。

题解:

首先可以知道号点只能和号点相连,号点可以和号点相连,号点可以和号点相连,所以可以发现号点本质上是相同的,因此可以只看成两种标号,因此问题就转化为二分图染色问题。题目不保证联通,因此对于每个联通块首先要满足不存在奇环,否则肯定不存在满足条件的方案。然后对于每个联通块,将它分为奇数层和偶数层,要求对于每个联通块的其中一个层相加的和刚好等于,就很类似背包问题,可以用dp来实现。在算每个联通块的时候,可以记录下其中一层的路径,那么最终就将构造的那一层标为,剩下的贪心标为即可

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 5005;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
int n, m, n1, n2, n3, p[maxn], x[maxn];
int cnt, d1[maxn], d2[maxn];
bool dp[maxn][maxn], rev[maxn];
void dfs(int u)
{
    if (x[u] == 1)
        d1[cnt]++;
    else
        d2[cnt]++;
    for (int v : G[u])
    {
        if (!x[v])
        {
            x[v] = 3 - x[u];
            p[v] = cnt;
            dfs(v);
        }
        else if (x[u] == x[v])
        {
            puts("NO");
            exit(0);
        }
    }
}
int main()
{
    scanf("%d%d", &n, &m);
    scanf("%d%d%d", &n1, &n2, &n3);
    for (int i = 0, x, y; i < m; i++)
    {
        scanf("%d%d", &x, &y);
        x--;
        y--;
        G[x].push_back(y);
        G[y].push_back(x);
    }
    cnt = 0;
    dp[0][0] = true;
    for (int i = 0; i < n; i++)
        if (!x[i])
        {
            p[i] = cnt;
            x[i] = 1;
            dfs(i);
            for (int j = d1[cnt]; j <= n2; j++)
                dp[cnt + 1][j] |= dp[cnt][j - d1[cnt]];
            for (int j = d2[cnt]; j <= n2; j++)
                dp[cnt + 1][j] |= dp[cnt][j - d2[cnt]];
            cnt++;
        }
    if (!dp[cnt][n2])
    {
        puts("NO");
        return 0;
    }
    puts("YES");
    while (cnt--)
    {
        rev[cnt] = !dp[cnt][n2 - d2[cnt]];
        if (rev[cnt])
            n2 -= d1[cnt];
        else
            n2 -= d2[cnt];
    }
    for (int i = 0; i < n; i++)
    {
        if (rev[p[i]])
            x[i] = 3 - x[i];
        if (x[i] == 2)
            putchar('2');
        else if (n1)
        {
            putchar('1');
            n1--;
        }
        else
            putchar('3');
    }
    puts("");
}