[置顶] 埃森哲杯上海大学春季赛暨金马五校赛题解汇总

76人阅读 评论(0) 收藏 举报
分类:

比赛时候卡在全场题L,心态很崩,后来其他题目思考不进去了。。。加上一个队友临时有事也没来训练。。。最后4题收尾。。。

补题补题,现在把暂时都切出来的题目放上题解和代码吧~

------------------------------------------------------------------------------------------------------------------


A:题意是只能对第一堆的土进行移动,求花费代价最小。

贪心求解,O(n)复杂度

首先让c[i] = a[i]-b[i],那么显然c[i]>0意味着第i号位置的土比较多,应该从c[i]>0的位置往c[j]<0的位置移动

用一个指针记录最左端c[i]>0的位置,现在应该用最靠左的土填满当前c[i]<0的位置,然后扫描一遍就A了~

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int maxn = 1e5+10;
int a[maxn],b[maxn],c[maxn];
int n;
typedef long long LL;

inline int Min(int x, int y) {return x<y?x:y; }
inline int Max(int x, int y) {return x>y?x:y; }

int main(){
    int T;
    scanf("%d",&T);
    while (T--){
        scanf("%d",&n);
        for (int i=1; i<=n; i++) scanf("%d",&a[i]);
        for (int i=1; i<=n; i++) scanf("%d",&b[i]);
        for (int i=1; i<=n; i++) c[i] = a[i] - b[i];

        LL ans = 0;
        int idx = 1;
        for (int i=1; i<=n; i++) if (c[i]<0) {
            while (c[i]<0) {
                while (idx<=n && c[idx]<=0) idx++;
                int t = Min(-c[i],c[idx]);
                ans += (LL)t*abs(i-idx);
                c[i] += t; c[idx] -= t;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


B:对于当前节点i,i*F(i) 相当于累加F(i)个i,F(i)是第i个节点有的合约数节点个数。

那么只要把i加到每个合约数节点后再统计即可。

说的有点不清楚,看代码里面的dfs就能懂的~

#include <cstdio>
#include <cstring>
#include <vector>
using namespace std;
 
typedef long long LL;
const int mod = 1e9+7;
const int maxn = 2e4+10;
vector<int>g[maxn];
vector<int>vec[maxn];
bool vis[maxn];
int w[maxn];
int n,p;
 
LL cnt[maxn];
LL ans;
 
void init(){
    for (int i=1; i<=10000; i++) vis[i] = 1;
    for (int i=2; i<=10000; i++) if (vis[i]) {
        for (int j=i+i; j<=10000; j+=i) vis[j] = 0;
    }
    for (int i=2; i<=10000; i++) if (vis[i]==0) {
         for (int j=i; j<=10000; j+=i) vec[j].push_back(i);
    }
}
 
void dfs(int u, int fa) {
    int v;
    for (int i=0; i<vec[w[u]].size(); i++) {
        v = vec[w[u]][i];
        cnt[v] = (cnt[v] + u + mod)%mod;
    }
    ans = (ans+cnt[w[u]])%mod;
    for (int i=0; i<g[u].size(); i++) {
        if (g[u][i] == fa) continue;
        dfs(g[u][i],u);
    }
 
    for (int i=0; i<vec[w[u]].size(); i++) {
        v = vec[w[u]][i];
        cnt[v] = (cnt[v] - u + mod)%mod;
    }
}
 
int main(){
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    init();
 
    while (T--) {
        ans = 0;
        scanf("%d%d",&n,&p);
        for (int i=1; i<=n; i++) g[i].clear();
        for (int i=1; i<=n; i++) cnt[i] = 0;
        int v,u;
        for (int i=1; i<n; i++) {
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        for (int i=1; i<=n; i++) scanf("%d",&w[i]);
        dfs(p,-1);
        printf("%lld\n",ans);
    }
    return 0;
}


C:n<=9,利用STL的next_permutation枚举全排列,然后统计转换次数

可以预处理a[i]转换为b[j]需要的操作次数,简单题

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 20;
const int INF = 1e9;
int a[maxn],b[maxn],p[maxn];
int cost[maxn][maxn];
int n;

int calc(int x, int y) {
    if (x == y) return 0;
    if (x < y) swap(x,y);

    int res = 0;
    while (x>y) {
        if (x%2 == 0) {
            if (x/2>=y) { x>>=1; res++; }
            else {res += x-y; return res; }
        }
        else {
            --x;
            res++;
        }
    }
    return res;
}

int t[maxn];
int solve(){
   memcpy(t,p,sizeof(t));
   int res = 0;
   for (int i=1; i<=n; i++) {
      if (t[i] != i) {
          for (int j=i+1; j<=n; j++) if (t[j] == i) {
              swap(t[j],t[i]);
              break;
          }
          res++;
      }
   }
   return res;
}

int main(){
    int T;
    scanf("%d",&T);
    while (T--){
       scanf("%d",&n);
       for (int i=1; i<=n; i++) scanf("%d",&a[i]);
       for (int j=1; j<=n; j++) scanf("%d",&b[j]);
       for (int i=1; i<=n; i++) p[i] = i;

       for (int i=1; i<=n; i++)
         for (int j=1; j<=n; j++) cost[i][j] = calc(a[i],b[j]);

       int ans = INF;
       do {
         int f = 0;
         for (int i=1; i<=n; i++) f += cost[p[i]][i];
         f += solve();
         if (f<ans) ans = f;
       }while (next_permutation(p+1,p+n+1));
       printf("%d\n",ans);
    }
    return 0;
}


D: 这题个人认为很难,无法证明结论,但写起来就几行代码。。

#include <cstdio>
using namespace std;

int L1,R1,L2,R2,mod;
int main(){
    int T;
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d%d%d%d",&L1,&R1,&L2,&R2,&mod);
        if (R2-L2+1>=mod) printf("LOSE\n");
        else printf("WIN\n");
    }
    return 0;
}


E:签到题~

#include <cstdio>
#include <map>
using namespace std;

typedef long long LL;
LL ans,n;

int main(){
    while (scanf("%lld",&n)==1){
        ans = (LL)1<<n;
        printf("%lld\n",ans);
    }
    return 0;
}


F:找规律题,可以发现满足要求的x转换为二进制后,不会有相邻两位都为1

假设当前枚举的二进制长度为x,满足题意的数字个数为f(x),能发现f(x)是斐波那契数列~

代码如下:

#include <cstdio>
#include <cstring>
using namespace std;

const int maxn = 70;
typedef long long LL;
LL f[maxn],sum[maxn];
LL n;

void init(){
   f[0] = f[1] = 1;
   for (int i=2; i<=60; i++) f[i] = f[i-1] + f[i-2];
   sum[0] = 1;
   for (int i=1; i<=60; i++) sum[i] = sum[i-1] + f[i];
}

int main(){
    init();
    //freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while (T--){
        scanf("%lld",&n);
        LL ans = 0;
        bool isfirst = 1;
        while (n>0) {
            if (isfirst) {
                if (n == 1) {
                    ans = 1LL;
                    break;
                }
                int i=0;
                while (sum[i]<n) i++;
                ans += 1LL<<i;
                n -= sum[i-1];
                isfirst = 0;
            }
            else {
                n = n-1;
                int i = 0;
                if ( n == 1) {
                    ans = ans+1;
                    break;
                } else if ( n == 0) {
                      break;
                }
                while (sum[i]<n) i++;
                ans += 1LL<<i;
                n -= sum[i-1];
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


G:待填坑

H:待填坑


I:签到题,不过不知道为什么官方题解那么说那么多。。。

也不理解为什么要用java或python写大数。。

很显然的,找到最高位奇数,然后修改就可以了。


假设字符串长度为n,最高奇数为在第i位。

1:第i位数字为9,把第i位数字减一,i+1到n位数字全部变为8

2:看第i位之后的数字是否都是4 或出现 若干个4然后接着一个比4小的数字,是的话第i位数字减一,后面i+1到n位全部为8

3:以上两种情况不满足,把第i位数字加一,后面全部变为0

代码如下:

#include <cstdio>
#include <cstring>
using namespace std;
 
const int maxn = 100000+10;
char s[maxn];
int n;
 
int main(){
    int T;
    scanf("%d",&T);
    while (T--){
        scanf("%s",s);
        n = strlen(s);
        int st = 0;
        while (st<n && (s[st]-'0')%2==0 ) st++;
 
        if (st>=n) {
            printf("%s\n",s);
            continue;
        }
 
        if (n == 1 && s[0] == '1') {
            printf("0\n");
            continue;
        }
 
        int nxt = st+1;
        while (nxt<n && s[nxt]=='4') nxt++;
        if (nxt>=n || s[nxt]<'4' || s[st]=='9') {
            s[st]--;
            for (int i=st+1; i<n; i++) s[i] = '8';
        }
        else {
            s[st]++;
            for (int i=st+1; i<n; i++) s[i] = '0';
        }
 
        int f = 0; while (f<n && s[f]=='0') f++;
        for (int i=f; i<n; i++) printf("%c",s[i]); printf("\n");
    }
    return 0;
}


J:二分+二分图最大匹配

最多N+1个空位,m个点向可以插入的空连边。有n+1-m个虚点,向|ai-1 - ai| <= len 的空连边 ,结果要求完全匹配~

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;

const int INF = 1e9+7;
const int maxn = 200+10;
int n,m;
int a[maxn],b[maxn];
int g[maxn][maxn];
int left[maxn];
bool vis[maxn];

bool match(int u) {
    for (int i=1; i<=m; i++) if (g[i][u] && !vis[i]) {
        vis[i] = 1;
        if (!left[i] || match(left[i])) {
            left[i] = u;
            return 1;
        }
    }
    return 0;
}


bool judge(int mid) {
     for (int i=1; i<=m; i++) {
         for (int j=1; j<=n+1; j++) {
              g[i][j] = 1;
              if (j>1 && abs(b[i]-a[j-1])>mid) g[i][j] = 0;
              if (j<n+1 && abs(b[i]-a[j])>mid) g[i][j] = 0;
         }
     }

     int ans = 0;
     for (int i=1; i<=n+1; i++) left[i] = 0;
     for (int i=2; i<=n; i++) {
         if (abs(a[i-1]-a[i])>mid) {
               for (int j=1; j<=m; j++) vis[j] = 0;
               if (!match(i)) return 0;
               ans++;
         }
     }

     for (int i=1; i<=n+1; i++) {
        if (i==1 || i==n+1 || abs(a[i-1]-a[i])<=mid) {
            for (int j=1; j<=m; j++) vis[j] = 0;
            ans += match(i);
        }
     }
     return ans == m;
}

int main(){
    int T;
    scanf("%d",&T);
    while (T--){
        scanf("%d%d",&n,&m);
        for (int i=1; i<=n; i++) scanf("%d",&a[i]);
        for (int j=1; j<=m; j++) scanf("%d",&b[j]);

        int left = 0, right = INF;
        int mid, ans = 0;
        while (left <= right) {
            mid = (left+right)>>1;
            if (judge(mid)) {
                right = mid-1;
                ans = mid;
            }
            else left = mid+1;
        }
        printf("%d\n",ans);
    }
    return 0;
}


K:待填坑


L:这题卡了N久,最后还没写出来,太菜了。。。据说出题人搞错了,数据当成连续子串也A了。。。

emmm,正解DP我还没想出来,平时练习DP太少了。。

先放上一个有毛病的AC代码,当成连续子串处理的。

#include <cstdio>
using namespace std;

typedef long long LL;
const int maxn = 1e5+10;
int a[maxn];
LL sum[maxn];
int n,k;

inline int Max(int x, int y) {return x>y?x:y; }
int main(){
    while (scanf("%d%d",&n,&k) == 2) {
        for (int i=1; i<=n; i++) {
            scanf("%d",&a[i]);
            a[i] %= k;
        }
        sum[0] = 0;
        for (int i=1; i<=n; i++) sum[i] = sum[i-1] + a[i];

        int mx = -1;
        for (int i=1; i<=n; i++) {
            for (int j=n; j>=i; j--) if ((sum[j]-sum[i-1])%k == 0) {
                mx = Max(mx,j-i+1);
                break;
            }
        }
        printf("%d\n",mx);
    }
    return 0;
}

查看评论

2017上海金马五校程序设计竞赛部分题解

Problem A : STEED Cards From: DHUOJ, 2017060301 Submit (Out of Contest) Time ...
  • moonlighttlj
  • moonlighttlj
  • 2017-06-03 20:30:13
  • 649

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛

五题菜狗… 那个自习室真是有毒..坐了一下子马上就感冒了..头痛… A Wasserstein Distance 这题也是搞不懂,一开始狂wa,迷迷糊糊的. #include &amp;lt;...
  • m0_37802215
  • m0_37802215
  • 2018-04-15 20:09:09
  • 122

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛E题小Y吃苹果

链接:https://www.nowcoder.com/acm/contest/91/E题意:小Y买了很多苹果,但他很贪吃,过了几天一下就吃剩一只了。每一天小Y会数出自己的苹果个数X,如果X是偶数,他...
  • weixin_36416680
  • weixin_36416680
  • 2018-04-15 20:20:20
  • 29

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛H题小Y与多米诺骨牌(线段树优化dp)

题意 题目链接:https://www.nowcoder.com/acm/contest/91/H 来源:牛客网 题解 设l[i]l[i]l[i]为向左推第iii个骨牌最远能影响到的骨牌的...
  • ME495
  • ME495
  • 2018-04-17 11:20:36
  • 85

埃森哲杯金马五校赛 L . K序列(dp)

链接:https://www.nowcoder.com/acm/contest/91/L来源:牛客网题目描述给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”...
  • qq_33997572
  • qq_33997572
  • 2018-04-17 19:39:04
  • 18

2018年全国多校算法寒假训练营练习比赛(第五场)G-送分啦-QAQ

链接:https://www.nowcoder.com/acm/contest/77/G来源:牛客网题目描述     幼儿园开学了,为了让小盆友们能尽可能的多的享受假期。校长大人决定让小盆友分批到校...
  • albertluf
  • albertluf
  • 2018-02-26 09:12:34
  • 55

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛F题 1+2=3?

题目链接:https://www.nowcoder.com/acm/contest/91/F题意:求第N个符合条件X^2X=3X的X。N&amp;lt;=10的12次方。解题思路:我刚开始想到最粗暴的...
  • weixin_36416680
  • weixin_36416680
  • 2018-04-16 11:01:00
  • 50

【牛客网】【埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛】B—合约数【莫队做法】

题目链接:B—合约数题意:一棵树,有n个节点,从1编号到n。根节点的编号为p。给出每个节点的val[i]值,定义f(i)为以编号i为根节点的子树中(包括根节点),所有val[j]是合数并且是val[i...
  • gymgym1212
  • gymgym1212
  • 2018-04-15 20:33:28
  • 132

第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛(部分题解)

唉,好久没更新博客了,说明我这段时间在学习上都懈怠了Σ( ° △ °|||)︴昨天打了场金马赛,这网络…..我真的是无力吐槽…..前三题都是大水题,就不挂代码了;D-快速幂取模裸题#include u...
  • Murphyc
  • Murphyc
  • 2017-07-10 13:54:47
  • 481

埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛A题Wasserstein Distance

题目链接:https://www.nowcoder.com/acm/contest/91/A题意:给两组数据,求把A变成B状态需花费的最小体力。思路:s=s+abs(a[i]-b[i]);a[i+1]...
  • weixin_36416680
  • weixin_36416680
  • 2018-04-15 20:11:56
  • 33
    个人资料
    持之以恒
    等级:
    访问量: 2万+
    积分: 2605
    排名: 1万+
    最新评论