2018 ICPC 南京

链接

A. Adrien and Austin

题意:

给定n个石头,每次可以取走连续的长度不超过K的连续石头,若无法进行操作即失败。

博弈论,若先手能将序列变成对称的两部分则必胜,否则必败。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int n, k;
    cin >> n >> k;
    if(k != 1){
        if(n) cout << "Adrien\n";
        else cout << "Austin\n";
    }
    else{
        if(n & 1) cout << "Adrien\n";
        else cout << "Austin\n";
    }
    return 0;
}

D. Country Meow

题意:

给定n个点的三维点集,求出该点集的最小球覆盖。

n \leq 100x,y,z \leq 10^5,可以使用三分套三分套三分的方法来求解。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 10;
struct point {
    double x, y, z;
}p[maxn];
const double eps = 1e-10;
int n;

double dist(point x, point y)
{
    return sqrt((x.x - y.x) * (x.x - y.x) + (x.y - y.y) * (x.y - y.y) + (x.z - y.z) * (x.z - y.z));
}

double cal(double x, double y, double z)
{
    double ans = 0;
    for ( int i = 1;i <= n;i++ ) {
        ans = max(ans, dist(point({ x,y,z }), p[i]));
    }
    return ans;
}

double solvez(double x, double y)
{
    double ans = 1e12;
    double l = -maxn, r = maxn;
    while ( r - l > eps ) {
        double lmid = l + (r - l) / 3;
        double rmid = r - (r - l) / 3;
        double temp1 = cal(x, y, lmid);
        double temp2 = cal(x, y, rmid);
        ans = min(ans, min(temp1, temp2));
        if ( temp1 < temp2 ) {
            r = rmid;
        }
        else l = lmid;
    }
    return ans;
}

double solvey(double x)
{
    double ans = 1e12;
    double l = -maxn, r = maxn;
    while ( r - l > eps ) {
        double lmid = l + (r - l) / 3;
        double rmid = r - (r - l) / 3;
        double temp1 = solvez(x, lmid);
        double temp2 = solvez(x, rmid);
        ans = min(ans, min(temp1, temp2));
        if ( temp1 < temp2 ) {
            r = rmid;
        }
        else l = lmid;
    }
    return ans;
}

double solvex()
{
    double ans = 1e12;
    double l = -maxn, r = maxn;
    while ( r - l > eps ) {
        double lmid = l + (r - l) / 3;
        double rmid = r - (r - l) / 3;
        double temp1 = solvey(lmid);
        double temp2 = solvey(rmid);
        ans = min(ans, min(temp1, temp2));
        if ( temp1 < temp2 ) {
            r = rmid;
        }
        else l = lmid;
    }
    return ans;
}

int main()
{
    scanf("%d", &n);
    for ( int i = 1;i <= n;i++ ) {
        scanf("%lf%lf%lf", &p[i].x, &p[i].y, &p[i].z);
    }
    printf("%.12f\n", solvex());
    return 0;
}

G. Pyramid

题意:

给定一个图中的n层的三角形,求出可以构成正三角形的三元点对个数。

找规律可得ans = C_{n + 3}^4,直接算即可。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int maxn = 1e5+10;
const int p = 1e9+7;
typedef long long ll;
int n;

ll ksm(ll x,ll y)
{
    ll ret = 1;
    do{
        if(y & 1)
            ret = ret * x % p;
        x = x * x % p;
    }while(y >>= 1);
    return ret;
}

int main()
{
    int _;
    scanf("%d",&_);
    while(_--){
        scanf("%d",&n);
        ll ans = 1ll * n * (n + 1) % p * (n + 2) % p * (n + 3) % p * ksm(24,p - 2) % p;
        printf("%lld\n",ans);
    }
    return 0;
}

I. Magic Potion

题意:

有n个勇士和m个怪物,每个勇士有一个可击杀的列表,每个英雄只可选择一个可击杀的怪物进行击杀,有k瓶药,每个勇士最多和一瓶,可以额外击杀一只怪物,求可击杀怪物的最大数量。

很明显的一个二分图匹配问题,对于勇士进行拆点,拆成没喝药的和喝了药的。然后建边跑个网络流即可。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 3e5+10;
const int maxm = 1e5+10;
struct Edge{
    int val, to, next;
}e[maxm << 1];
int head[maxn], tot = 1;
int cur[maxn];
int k,num,temp;
int dep[maxn];
int n,m;
int s,t;

void addedge(int u,int v,int w)
{
    e[++tot]=Edge({w,v,head[u]});
    head[u] = tot;
    e[++tot]=Edge({0,u,head[v]});
    head[v] = tot;
}

int bfs()
{
    for(int i = 1;i<=n;i++){
        dep[i] = 0;
        cur[i] = head[i];
    }
    // memset(dep,0,sizeof dep);
    // memcpy(cur,head,sizeof head);
    queue<int> qq;
    qq.push(s);
    dep[s] = 1;
    while(!qq.empty()){
        int temp = qq.front();
        qq.pop();
        for(int i = head[temp];~i;i=e[i].next){
            int to = e[i].to;
            if(e[i].val && !dep[to]){
                dep[to] = dep[temp] + 1;
                if(to == t){
                    return 1;
                }
                qq.push(to);
            }
        }
    }
    return 0;
}

int dfs(int beg,int flow)
{
    if(beg == t){
        return flow;
    }
    int ret = 0;
    for(int &i = cur[beg];~i;i=e[i].next){
        int to = e[i].to;
        if(e[i].val && dep[to] == dep[beg] + 1){
            int res = dfs(to,min(e[i].val,flow));
            ret += res;
            e[i].val -= res;
            e[i^1].val += res;
            if((flow-=ret) <= 0)
                break;
        }
    }
    if(ret == 0)
        dep[beg] = 0;
    return ret;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    memset(head,-1,sizeof head);
    for(int i = 1;i<=n;i++){
        scanf("%d",&num);
        for(int j = 1;j<=num;j++){
            scanf("%d",&temp);
            addedge(i,2 * n + temp,1);
            addedge(n + i,2 * n + temp,1);
        }
    }
    s = 2 * n + 2 * m + 1;
    int s1 = s + 1, s2 = s + 2;
    t = s + 3;
    for(int i = 1;i <= m;i++){
        addedge(2 * n + m + i,t,1);
        addedge(2 * n + i , 2 * n + m + i, 1);
    }
    addedge(s,s1,n);
    addedge(s,s2,k);
    for(int i = 1;i<=n;i++){
        addedge(s1,i,1);
        addedge(s2,n + i,1);
    }
    n = 2 * n + 2 * m + 4;
    int ans = 0;
    while(bfs()){
        ans += dfs(s,0x3f3f3f3f);
    }
    printf("%d",ans);
    return 0;
}

J. Prime Game

题意:

给定n个数的序列,求所有子段内的乘积的不同质因子个数和。

共有n^2个子段,考虑计算每个质数的贡献。用埃氏筛预处理出每个质数的倍数出现的位置,然后枚举质数将没有贡献的区间数减去即可。

#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 2e6 + 10;
typedef long long ll;
#define int ll
vector<int> pos[maxn];
int flag[maxn];
int p[maxn];
vector<int> pp[maxn];
int prime[maxn];
int n, a[maxn];

void pre()
{
    p[1] = 1;
    for (int i = 2; i < maxn; i++)
    {
        if (!p[i])
        {
            prime[++prime[0]] = i;
            pos[i].push_back(0);
            if (flag[i])
            {
                for (int j = 0; j < (int)pp[i].size(); j++)
                {
                    pos[i].push_back(pp[i][j]);
                }
            }
            int j = 2 * i;
            while (j < maxn)
            {
                p[j] = 1;
                if (flag[j])
                {
                    for (int k = 0; k < (int)pp[j].size(); k++)
                    {
                        pos[i].push_back(pp[j][k]);
                    }
                }
                j += i;
            }
            pos[i].push_back(n + 1);
        }
    }
}

signed main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        flag[a[i]] = 1;
        pp[a[i]].push_back(i);
    }
    pre();
    ll tt = 0;
    for (int i = 1; i <= prime[0]; i++)
    {
        int temp = prime[i];
        ll ans = n * (n + 1) / 2;
        if (pos[temp].size() == 2)
        {
            continue;
        }
        sort(pos[temp].begin(), pos[temp].end());
        for (int j = 1; j < (int)pos[temp].size(); j++)
        {
            ll temp1 = pos[temp][j];
            ll temp2 = pos[temp][j - 1];
            ans -= (temp1 - temp2) * (temp1 - temp2 - 1) / 2;
        }
        tt += ans;
    }
    printf("%lld\n", tt);
    return 0;
}

K. Kangaroo Puzzle

题意:

给定一个网格图和若干只袋鼠,每次可使所有袋鼠向同一个方向移动一格,求出一个序列使得按照这个序列进行操作后,所有袋鼠都在同一格。

据说正解是搜索,我们用随机水过去的。位置不同的袋鼠数一定是单调不减的,网格图最大只有20*20,输出序列长度可达50000,直接随机输出即可。

#include<bits/stdc++.h>
using namespace std;
char str[100][10000];
int a,b,c,d,e;
int main()
{
    cin>>a>>b;
    for(int i=1; i<=a; i++)
    {
        cin>>str[i];
    }
    for(int i=1; i<=49999; i++)
    {
        int p=rand()%4;
        if(p==0)
            cout<<"U";
        else if(p==1)
            cout<<"D";
        else if(p==2)
            cout<<"L";
        else
            cout<<"R";
    }
    cout<<endl;
    return 0;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值