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

比赛时候卡在全场题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;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值