XCPC Team Trainning Round 1 (NCPC2018)

补题地址

EOJ
CF

榜单&补题情况
在这里插入图片描述
题解、分析和code一部分是自己写的,一部分是抄的队友的。

A. Altruistic Amphibians 01背包

题意

给出n只青蛙和洞的高度d,每只青蛙有l、w、h三个属性,分别表示跳跃高度、重量和身高。青蛙可以像叠罗汉一样叠在一起,但要求每只青蛙身上的重量小于它自身。当某只青蛙的跳跃高度+身下叠起来的高度>d时,青蛙可以跳出洞,问最多几只青蛙能跳出去?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBC0FMuF-1600494395120)(C:\Users\MorphLing\AppData\Roaming\Typora\typora-user-images\image-20200918001314113.png)]

分析

首先考虑如何安排青蛙跳出的顺序。最轻的青蛙无法提供任何高度,因此可以安排第一个跳出去,而重量最大的青蛙只能靠自己跳出去,所以一定是最后一个。于是结论是一个青蛙能否跳出去,只取决于重量比它大的青蛙的属性,因此可以将青蛙按照重量从大到小排序,依次决定是否能产生贡献。

此时很明显是一个01背包问题,只要用 d p [ i ] dp[i] dp[i]来表示重量为 i i i时,其他青蛙所能提供的最大高度就能做出来了。读题的时候感觉数据范围比较玄学就没继续往下想,忽略了题目中一个重要条件就是所有青蛙重量 w w w之和小于 1 e 8 1e8 1e8,那么对于第 i i i只青蛙,其重量为 w i w_i wi,只要修改 [ 1 , w i − 1 ] [1,w_i-1] [1,wi1]范围内的 d p dp dp,就相当于考虑到借助第 i i i只青蛙后 d p dp dp数组的变化了。一个小优化是令 N = 1 e 8 N=1e8 N=1e8,那么只需要考虑 [ 1 , m i n ( w i − 1 , N − w i ) ] [1,min(w_i-1,N-w_i)] [1,min(wi1,Nwi)]这个范围即可,因为除了 i i i之外剩下青蛙重量之和不超过 N − w i N-w_i Nwi,对于大于 N − w i N-w_i Nwi的情况就不需要记录其dp值了。

Code

#include<bits/stdc++.h>
using namespace std;

const int N = 1e8 + 5, M = 1e5 + 5;

struct frog {
    int l, w, h;
}a[M];

int n, d, dp[N], ans; //dp[i]表示重量为i时,其他青蛙所能提供的最大高度

int main() {
    cin >> n >> d;
    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d", &a[i].l, &a[i].w, &a[i].h);
    }
    sort(a + 1, a + 1 + n, [&](frog &u, frog &v){
        return u.w > v.w;
    });
    int mx = a[1].w;
    for (int i = 1; i <= n; i++) {
        if (dp[a[i].w] + a[i].l > d) ans++;
        for (int j = 1; j <= min(a[i].w - 1, N - a[i].w); j++) { //由于总重量之和不超过N,除了i之外剩下青蛙重量之和不超过N-a[i].w
            dp[j] = max(dp[j], dp[a[i].w + j] + a[i].h); //判断重量为j时,借助第i个青蛙后能否更高
        }
    }
    printf("%d\n", ans);
    return 0;
}

B. Baby Bites 签到

Code

#include<bits/stdc++.h>
typedef long long LL;

using namespace std;
int n;
char s[100];

int getnum(char *s) {
    int ans=0;
    int len=strlen(s);
    for (int i=0;i<len;i++) {
        ans=ans*10+s[i]-'0';
    }
    return ans;
}
int main()
{
    cin>>n;
    bool a=1;
    for (int i=1;i<=n;i++) {
        scanf("%s",s);
        if (strcmp(s,"mumble")==0) continue;
        else {
            if (getnum(s)!=i) a=0;
        }
        if (!a) break;
    }
    if (!a) printf("something is fishy\n");
    else printf("makes sense\n");
    return 0;
}

C. Code Cleanups 签到

Code

#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;

int n,a,b,c,d,x,y;
bool dir[1111];
int main()
{
    cin>>n;
    rep(i,1,n)
    {
        int x;
        cin>>x;
        dir[x]=true;
    }
    int dirt=0,cnt=0,ans=0;
    rep(day,1,365)
    {
        dirt+=cnt;
        if (dir[day]) cnt++;
        if (dirt+cnt>=20)
        {
            cnt=0;dirt=0;ans++;
        }
    }
    if (cnt) ans++;
    cout<<ans<<endl;
    return 0;
}

D. Delivery Delays 二分答案+DP

题意&分析

在这里插入图片描述
在这里插入图片描述

Code

#include <bits/stdc++.h>
#define debug(x) cerr << #x << ":" << x << endl
using namespace std;
typedef long long LL;

const int N = 1e3 + 5;

const LL INF = 0x3f3f3f3f3f3f3f3f;

LL n, m, k, s[N], u[N], t[N];

LL dis[N][N], dp[N], sumdis[N];

struct Dijkstra {
    struct Edge {
        int to; LL val;
    };
    struct Node {
        LL dis; int key;
        friend bool operator < (const Node &x, const Node &y) {
            return x.dis > y.dis;
        }
    };
    bool vis[N];
    int n, s;
    LL dis[N];
    vector<Edge>G[N];
    priority_queue<Node>Q;
    void init(int _n, int _s) {
        n = _n, s = _s;
        for (int i = 1; i <= n; i++) dis[i] = INF;
        for (int i = 1; i <= n; i++) vis[i] = 0;
    }
    void addEdge(int from, int to, LL val) {
        G[from].push_back({to, val});
    }
    void go() {
        dis[s] = 0;
        Q.push({0,s});
        while (!Q.empty()) {
            Node top = Q.top(); Q.pop();
            int now = top.key;
            if (vis[now]) continue;
            vis[now] = 1;
            for (int i = 0; i < G[now].size(); i++) {
                int to = G[now][i].to;
                if (dis[to] > dis[now] + G[now][i].val) {
                    dis[to] = dis[now] + G[now][i].val;
                    if (!vis[to]) Q.push({dis[to], to});
                }
            }
        }
    }
}dj;

bool check(LL x) {
    memset(dp, INF, sizeof(dp));
    dp[0] = 0;
    for (int i = 1; i <= k; i++) {
        LL latest = INF;
        for (int j = i - 1; j >= 0; j--) {
            if (j == i - 1) latest = s[j + 1] + x - dis[1][u[j + 1]];
            else latest = min(latest + dis[1][u[j + 2]] - dis[1][u[j + 1]] - dis[u[j + 1]][u[j + 2]], s[j + 1] + x - dis[1][u[j + 1]]);
            if (latest < max(dp[j], t[i])) continue;
            dp[i] = min(dp[i], max(dp[j], t[i]) + dis[1][u[j + 1]] + sumdis[i] - sumdis[j + 1]);
        }
        if (dp[i] > s[i] + x) return false;
        dp[i] += dis[1][u[i]];
    }
    return true;
}

int main() {
    cin >> n >> m;
    for (int i = 1; i <= m; i++) {
        int u, v, d;
        scanf("%d %d %d", &u, &v, &d);
        dj.addEdge(u, v, d);
        dj.addEdge(v, u, d);
    }
    for (int i = 1; i <= n; i++) {
        dj.init(n, i);
        dj.go();
        for (int j = i; j <= n; j++) dis[i][j] = dis[j][i] = dj.dis[j];
    }
    cin >> k;
    u[0] = 1;
    for (int i = 1; i <= k; i++) {
        scanf("%d %d %d", &s[i], &u[i], &t[i]);
        if (i == 1) continue;
        sumdis[i] = sumdis[i - 1] + dis[u[i - 1]][u[i]];
    }
    LL l = 0, r = INF;
    check(6);
    while(l < r) {
        LL mid = (l + r) >> 1;
        if (check(mid)) {
            r = mid;
        } else l = mid + 1;
    }
    cout << l;
    return 0;
}

E. Exploson Exploit 记忆化搜索

题意&分析

在这里插入图片描述

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef array<int,7> arr;
typedef pair<arr,arr> pii;

map<pii,double>dp;

arr st1,st2,ast1,ast2;

int n,m,d;

double dfs(arr st1,arr st2,int nzero,int p,int q) {
    if (dp.find({st1,st2})!=dp.end()) return dp[{st1,st2}];
    if (p==0) return 1;
    if (q==0) return 0;
    double res=0;
    for (int i=1;i<=6;i++) {
        for (int j=1;j<=st1[i];j++) {
            ast1=st1,ast2=st2;
            if (st1[i]==0) continue;
            ast1[i]--;
            ast1[i-1]++;
            res+=dfs(ast1,ast2,nzero-(i==1),p,q-1)/nzero;
        }
    }
    for (int i=1;i<=6;i++) {
        for (int j=1;j<=st2[i];j++) {
            ast1=st1,ast2=st2;
            if (st2[i]==0) continue;
            ast2[i]--;
            ast2[i-1]++;
            res+=dfs(ast1,ast2,nzero-(i==1),p-(i==1),q-1)/nzero;
        }
    }
    return dp[{st1,st2}]=res;
}
int main() {
    cin>>n>>m>>d;
    for (int i=1;i<=n;i++) {
        int x;
        scanf("%d",&x);
        st1[x]++;
    }
    for (int i=1;i<=m;i++) {
        int x;
        scanf("%d",&x);
        st2[x]++;
    }
    cout.precision(20);
    cout << dfs(st1,st2,n+m,m,d);
    return 0;
}

H. House Lawn

一开始题意没读清,以为要选多个除草机,但其实只要选一个就行了。每个除草机运行以(t+r)周为周期,那么枚举0~t+r,保证每周平均下来都能完成一次就可以了。

Code

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=1e6+5,INF=0x3f3f3f3f;
int l,m,t,p,len;
char s[300];
struct mow{
    string name;
    int p,c,t,r,ord;
}a[N];
void readname(int now) {
    string t;
    while(s[p]!=',') {
        t+=s[p];
        p++;
    }
    a[now].name=t;
    p++;
}
void readnum(int &now) {
    int res=0;
    while(s[p]!=',' && p<len) {
        res=res*10+s[p]-'0';
        p++;
    }
    now=res;
    p++;
}

bool check(int now) {
    bool res=1;
    for (int i=1;i<=a[now].r+a[now].t;i++) {
        LL time=1ll*i*10080;
        LL worktime=time/(a[now].t+a[now].r)*a[now].t;
        LL lfttime=time%(a[now].t+a[now].r);
        worktime+=min(lfttime,1ll*a[now].t);
        if (1ll*worktime*a[now].c>=1ll*l*i) res=1;
        else res=0;
        if (!res) break;
    }
    return res;
}
vector<string>ans;
int main()
{
    cin>>l>>m;
    getchar();
    for (int i=1;i<=m;i++) {
        gets(s);
        len=strlen(s);
        p=0;
        readname(i);
        readnum(a[i].p);
        readnum(a[i].c);
        readnum(a[i].t);
        readnum(a[i].r);
        a[i].ord=i;
    }
    sort(a+1,a+1+m,[&](mow a,mow b) {
        if (a.p!=b.p)
            return a.p>b.p;
        else return a.ord<b.ord;
    });
    int mincost=INF;
    for (int i=1;i<=m;i++) {
        if (check(i)) {
            if (a[i].p==mincost) {
                ans.push_back(a[i].name);
            }
            else {
                ans.clear();
                ans.push_back(a[i].name);
                mincost=a[i].p;
            }
        }
    }
    if (ans.size()==0) printf("no such mower");
    else for (auto x:ans) cout<<x<<endl;
    return 0;
}

I. Intergalactic Bidding 大数

因为后一个至少是前一个的两倍,所以手写大数,从大到小扫一遍能拿就拿。

Code

#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;
//template <typename T>
//void read(T &x){}
//template <typename T,typename U>
//void read(T &x,U &y){read(x);read(y);}
int n,tot;
char ans[2111][50];
struct BIGINT
{
    int dig;//number of digits
    int num[2111];
    char name[50];
    inline void read()
    {
        char inp[2111];
        cin>>inp;
        dig=strlen(inp);
        per(i,dig-1,0)
        {
            num[dig-i]=inp[i]-'0';
        }
    }
    bool operator<(const BIGINT &y)
    {
        if (dig<y.dig) return true;
        else if (dig>y.dig) return false;
        per(i,dig,1)
        {
            if (num[i]<y.num[i]) return true;
            else if (num[i]>y.num[i]) return false;
        }
        return false;
    }
    void operator-=(const BIGINT &y)
    {
        bool flag=false;
        rep(i,1,y.dig)
        {
            num[i]-=y.num[i];
            if (flag) {num[i]--;flag=false;}
            if (num[i]<0) {flag=true;num[i]=num[i]+10;}
        }
        if (flag) num[y.dig+1]--;
        while (!num[dig]&&dig>0) dig--;
    }
    void output()
    {
        per(i,dig,1) cout<<num[i];
        cout<<endl;
    }
    bool zero()
    {
        if (dig==0) return true;
        return false;
    }
    bool eq(const BIGINT &y)
    {
        if (y.dig!=dig) return false;
        rep(i,1,dig) if (y.num[i]!=num[i]) return false;
        return true;

    }
}a[2111],s;
int main()
{
    cin>>n;
    s.read();
    rep(i,1,n)
    {
        cin>>a[i].name;
        a[i].read();
    }
    sort(a+1,a+n+1);
    per(i,n,1)
    {
        if (a[i]<s||a[i].eq(s))
        {
            strcpy(ans[++tot],a[i].name);
            s-=a[i];
        }
    }
    if (!s.zero())
    {
        cout<<0<<endl;
        return 0;
    }
    cout<<tot<<endl;
    rep(i,1,tot) cout<<ans[i]<<endl;
    return 0;
}

J. Jumbled String 构造,分类

a=0时情况比较特殊,“1”的个数可能为1或者0,所以单独讨论一下个数为0的情况

Code

#include<bits/stdc++.h>
typedef long long LL;
#define lowbit(x) ((x)&-(x))
#define rep(x,y,z) for(int (x)=(y);(x)<=(z);(x)++)
#define per(x,y,z) for(int (x)=(y);(x)>=(z);(x)--)
using namespace std;
int n,a,b,c,d,x,y;
char s[100];

int check(int x) {
    if (x==0) return 1;
    x*=2;
    int t=(1+sqrt(1+4ll*x))/2;
    if (1ll*t*(t-1)==x)  return t;
    else return -1;
}
int main()
{
    cin>>a>>b>>c>>d;
    if (a==0 && d==0) {
        if (b==1 && c==0) {
            printf("01");
            return 0;
        }
        if (b==0 && c==1) {
            printf("10");
            return 0;
        }
        if (b==0 && c==0) {
            printf("0");
            return 0;
        }
        printf("impossible");
        return 0;
    }
    if (a==0) {
        if (b==0 && c==0) {
            y=check(d);
            if (y==-1) {
                printf("impossible");
                return 0;
            }
            for (int i=1;i<=y;i++) printf("1");
            return 0;
        }
    }
    if (d==0) {
        if (b==0 && c==0) {
            x=check(a);
            if (x==-1) {
                printf("impossible");
                return 0;
            }
            for (int i=1;i<=x;i++) printf("0");
            return 0;
        }
    }
    x=check(a);
    y=check(d);
    if (x==-1 || y==-1) {
        printf("impossible");
        return 0;
    }
    if ((b+c)!=(1ll*x*y)) {
        printf("impossible");
        return 0;
    }
    for (int i=1;i<=b/y;i++) printf("0");
    for (int i=1;i<=y-b%y;i++) printf("1");
    if (b%y!=0) printf("0");
    for (int i=1;i<=b%y;i++) printf("1");
    for (int i=1;i<=x-b/y-(b%y!=0);i++) printf("0");
    return 0;
}

K. King’s Colors 组合数学

题意

一颗大小为n的树,要求用恰好k种颜色涂色,且相邻两个点颜色不同,问方案数。

分析

很容易得到 用小于等于k种颜色涂色的方案数为 k ( k − 1 ) n − 1 k(k-1)^{n-1} k(k1)n1 ,与树的形状是无关的
一开始想到用{小于等于k种颜色涂色}-{小于等于k-1种颜色涂色}就是答案了,但是发现后面一项还需要乘一个 ( k k − 1 ) \binom{k}{k-1} (k1k)(从k种颜色里选出k-1种),结果这一乘就出问题了。因为会使得用小于k-1种颜色涂色的情况被重复计算。(似乎是犯了一个非常初级的组合数学错误。。)
于是考虑到题目数据范围很小,完全可以从1开始,求出用恰好k种颜色涂色的方案数,再一项一项去掉即可。

Code

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int maxn = 3e3;
const int mod = 1e9+7;
vector<int> g[maxn];

int c[maxn][maxn];
void init()
{
    c[0][0] = 1;
    for(int i = 1; i < maxn; i++)
    {
        c[i][0] = 1;
        for(int j = 1; j < maxn; j++)
        {
            c[i][j] = c[i - 1][j] + c[i - 1][j - 1];
            c[i][j] = c[i][j] % mod;
        }
    }
}
int ans[maxn];
int main()
{
    init();
    int n,k;
    scanf("%d %d", &n, &k);
    for(int i = 1; i < n; i++)
    {
        int x;
        scanf("%d", &x);
        g[x].push_back(i);
    }
    for(int i = 1; i <= k; i++)
    {
        int x = i;
        for(int j = 1; j < n; j++)
            x = 1ll * x * (i - 1) % mod;
        for(int j = 1; j < i; j++)
            x = ((x - 1ll * c[i][j] * ans[i - j] % mod) + mod) % mod;
        ans[i] = x;
    }
    cout << ans[k] << endl;
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值