2017 CCPC 秦皇岛站现场赛 【7/13】

题目链接

A - Balloon Robot

如果机器人从x开始某队需要等待的时间是t,那么如果从x+1开始,需要等待的时间就是(t-1+m)modm。由此我们可以先处理出机器人从1开始的等待时间,然后排序之后依次扫过去取结果的最小值,理论的复杂度为O(m+plogp)

但是发现,有很多点是一定不会成为最优解的。可以证明只有在p个队伍中出现过的点才有可能会成为最优解,这样总的复杂度就只有O(plogp)

#include 
using namespace std;
typedef long long ll;

const int maxn = 100050;

int t, n, m, p;
int a, b, pos[maxn];
ll res[maxn];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%d%d", &n, &m, &p);
        for(int i = 1;i <= n;i++)
            scanf("%d", &pos[i]);
        ll ans = 0;
        for(int i = 1;i <= p;i++)
        {
            scanf("%d%d", &a, &b);
            if(b%m <= pos[a]-1) res[i] = pos[a] - 1 - b%m;
            else res[i] = m - (b%m - pos[a] + 1);
            ans += res[i];
        }
        sort(res+1, res+p+1);
        res[0] = -1;
        ll cnt = ans;
        for(int i = 1;i <= p;i++)
        {
            if(res[i] == res[i-1]) continue;
            ll now = cnt - 1LL*res[i]*p + 1LL*m*(i-1);
            ans = min(ans, now);
        }
        printf("%lld\n", ans);
    }
    return 0;
}

C - Crusaders Quest

直接把串hash掉暴搜即可。

#include 
using namespace std;
typedef long long ll;

const int maxn = 15;

int t, ans;
char s[maxn];
map mp;

void dfs(int now, int num)
{
    if(now == 0) {ans = max(num, ans); return;}
    int tmp = now, no = 0;
    int p[maxn];
    while(tmp) {p[no++] = tmp % 4, tmp /= 4;}
    int pos = 0, tp;
    while(pos < no)
    {
        tp = pos;
        while(tp < no && p[tp] == p[pos]) tp++;
        int nxt = 0;
        for(int i = 0;i < no;i++)
        {
            if(i >= pos && i < tp) continue;
            nxt *= 4;
            nxt += p[i];
        }
        if(tp - pos == 3) dfs(nxt, num+1);
        else dfs(nxt, num);
        pos = tp;
    }
}

int main()
{
    scanf("%d", &t);
    mp['g'] = 1, mp['a'] = 2, mp['o'] = 3;
    while(t--)
    {
        scanf("%s", s);
        int tmp = 0;
        for(int i = 0;i < 9;i++)
        {
            tmp *= 4;
            tmp += mp[s[i]];
        }
        ans = 0;
        dfs(tmp, 0);
        printf("%d\n", ans);
    }
    return 0;
}

E - String of CCPC

由于第i次添加的代价是i-1且每个CCPC子串的贡献均为1,可以证明取最优解时最多添加一个字符。

预处理标记已存在的串以后扫一遍查找是否存在不冲突的子串即可。

#include 
using namespace std;
typedef long long ll;

const int maxn = 200050;

int t, n;
char s[maxn];
bool vis[maxn];

int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%s", &n, s);
        memset(vis, 0, sizeof(vis));
        int ans = 0;
        for(int i = 0;i < n-3;i++)
        {
            if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'P' && s[i+3] == 'C')
                ans++, vis[i] = 1;
        }
        bool flag = 0;
        for(int i = 0;i < n;i++)
        {
            if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'C' && !vis[i+1])
                flag = 1;
            if(s[i] == 'C' && s[i+1] == 'P' && s[i+2] == 'C' && (!vis[i-1]||i==0))
                flag = 1;
            if(s[i] == 'C' && s[i+1] == 'C' && s[i+2] == 'P' && !vis[i])
                flag = 1;
        }
        ans += flag;
        printf("%d\n", ans);
    }
    return 0;
}

G - Numbers

既然要使按位或的结果最小,就尽量使较高位上全部都为0。考虑直接构造或的结果,从高位到低位贪心。对于第i位,判断m个数的所有低位全部填满是否比n大。

如果比n小,那这一位就只能填1并且n对1<

用java的BigInteger实现。

import java.util.Scanner;
import java.math.BigInteger;

public class Main
{
    public static void main(String args[])
    {
        Scanner cin = new Scanner(System.in);
        int t = cin.nextInt();
        BigInteger zer = new BigInteger("0");
        BigInteger one = new BigInteger("1");
        BigInteger two = new BigInteger("2");
        for(int i = 0;i < t;i++)
        {
            BigInteger n = cin.nextBigInteger();
            BigInteger m = cin.nextBigInteger();
            BigInteger mi = new BigInteger("1");
            while(mi.subtract(one).multiply(m).compareTo(n) <= 0) mi = mi.multiply(two);
            BigInteger ans = new BigInteger("0");
            while(n.compareTo(zer) > 0)
            {
                while(mi.subtract(one).multiply(m).compareTo(n) >= 0) mi = mi.divide(two);
                BigInteger tmp =mi.multiply(m);
                ans = ans.add(mi);
                if(tmp.compareTo(n) < 0) n = n.subtract(tmp);
                else n = n.mod(mi);
            }
            System.out.println(ans);
        }
    }
}

H - Prime Set

容易想到如果两个数之和为素数,那么这两个数的奇偶性一定不同。由此可以将给出的数分成奇偶两部分,然后相加为素数的数之间连边,构成了一个二分图。

由于要求数目尽可能大,首先对这个图求最大匹配。然后根据最大匹配与k的关系来讨论。如果小于k,就从未匹配的点中再往里连。一个匹配的贡献是2,而其余点的贡献是1。

#include 
using namespace std;
typedef long long ll;

const int maxn = 3050;
const int maxm = 2000050;

int t, n, m, a[maxn], linker[maxn];
bool flag[maxn], pri[maxm], vis[maxn];
vectormaze[maxn];

void init()
{
    memset(pri, 0, sizeof(pri));
    pri[1] = 1;
    for(ll i = 2;i < maxm;i++)
    {
        if(pri[i]) continue;
        for(ll j = i*i; j < maxm;j += i)
            pri[j] = 1;
    }
}

bool dfs(int u)
{
    vis[u] = 1;
    int len = maze[u].size();
    for(int i = 0;i < len;i++)
    {
        int v = maze[u][i];
        if(vis[v]) continue;
        vis[v] = 1;
        if(linker[v] == -1 || dfs(linker[v]))
        {
            linker[u] = v;
            linker[v] = u;
            return true;
        }
    }
    return false;
}

int hungary()
{
    memset(linker, -1, sizeof(linker));
    int res = 0;
    for(int i = 1;i <= n;i++)
    {
        if(flag[i] && linker[i] == -1)
        {
            memset(vis, 0, sizeof(vis));
            if(dfs(i)) res++;
        }
    }
    return res;
}

int main()
{
    scanf("%d", &t);
    init();
    while(t--)
    {
        scanf("%d%d", &n, &m);
        for(int i = 0;i < maxn;i++)
            maze[i].clear();
        for(int i = 1;i <= n;i++)
            scanf("%d", &a[i]);
        memset(flag, 0, sizeof(flag));
        for(int i = 1;i <= n;i++)
        {
            for(int j = 1;j <= n;j++)
            {
                if(!pri[a[i]+a[j]])
                {
                    flag[i] = flag[j] = 1;
                    maze[i].push_back(j);
                    maze[j].push_back(i);
                }
            }
        }
        int ans = hungary();
        if(ans >= m) printf("%d\n", m*2);
        else
        {
            int cnt = 0;
            for(int i = 1;i <= n;i++)
            {
                if(flag[i] && linker[i] == -1)
                    cnt++;
            }
            ans = ans*2 + min(m - ans, cnt);
            printf("%d\n", ans);
        }
    }
    return 0;
}

L - One-Dimensional Maze

统计左边的R数目和右边的L数目取最小值即可。

M - Safest Buildings

对于一个点,下一轮安全区刷新的可行圆心区域是一个圆,和原安全区面积交最大的点就是存活概率最大的点。

为了避免精度丢失,可以将面积交转化为距离来判断。与圆心距离dis < R - 2*r的必定安全,其余的点就可以用这个距离来表示。

#include 
using namespace std;
typedef long long ll;

const int maxn = 1050;
const double pi = acos(-1.0);

int t, n;
double r, R;
struct node
{
    double x, y;
    int id;
    double s;
}e[maxn];
bool cmp(node a, node b)
{
    if(fabs(a.s - b.s) < 1e-6) return a.id < b.id;
    return a.s < b.s;
}
int main()
{
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d%lf%lf", &n, &R, &r);
        e[n+1].s = -1.0;
        double tp = (R - 2*r)*(R - 2*r);
        for(int i = 1;i <= n;i++)
        {
            scanf("%lf%lf", &e[i].x, &e[i].y);
            e[i].id = i;
            double dis = e[i].x*e[i].x + e[i].y*e[i].y;
            if(dis < tp) e[i].s = 0;
            else e[i].s = dis;
        }
        sort(e+1, e+1+n, cmp);
        int ans = 1;
        for(int i = 1;i <= n+1;i++)
        {
            if(fabs(e[i].s - e[1].s) > 1e-6)
            {
                ans = i-1;
                break;
            }
        }
        if(r == R) ans = n;
        printf("%d\n", ans);
        for(int i = 1;i <= ans;i++)
        {
            printf("%d", e[i].id);
            if(i == ans) printf("\n");
            else printf(" ");
        }
    }
    return 0;
}

 

你可能感兴趣的:(日常乱搞,ACM-ICPC)