【题解】牛客练习赛76

A.校园活动

因为如果能够进行分组,那么每个组的总熟悉度是相同的

所以枚举最左边的组的总熟悉度的所有可能进行验证,特判所有都为0的情况。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n;
int sum[1005];
string s;
int solve()
{
    if(sum[n]==0)
        return n;
    for(int i=1;i<n&&sum[n]-sum[i];i++)
    {
        int tmp=sum[i];
        int tmpsum=0;
        for(int i=1;i<=n;i++)
        {
            tmpsum+=s[i-1]-48;
            if(tmpsum==tmp)
                tmpsum=0;
            if(tmpsum>tmp)
                break;
        }
        if(tmpsum==0)
            return sum[n]/tmp;
    }
    return -1;
}
int main()
{
    cin >> n;
    cin >> s;
    for(int i=0;i<n;i++)
        sum[i+1]=sum[i]+s[i]-48;
    cout << solve() << endl ;
    return 0;
}

B.zzugzx (vs) Kurisu

记忆化搜索+简单的算期望(另外看到有人是直接递推的,没细看,大家可以去康康)

注意到这个范围给的很奇葩

因为要根据对手和自己当前的状况来决策,所以维护两个进制数几乎是必须的

然后如果在一个位置填上,仍然不能判断是否被填充,所以把映射到变成进制数

表示当前先手数字为,后手数字为的获胜概率,然后去记忆化暴搜

每次枚举当前摇到的点数,然后对可能放置的每一个位置取最大(先手),取最小(后手)

个人认为不恐怖但很有意思的一题!!!

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
double f[5009][5009];
int n,m,ok[5009][5009];
double dfs(int tim,int a,int b)
{
    if( tim==0 )    return 1.0*(a>b);
    if( ok[a][b] )    return f[a][b];
    ok[a][b] = 1;
    if( tim%2==0 )
    {
        for(int i=1;i<=m;i++)
        {
            double maxx = 0; int now = a, base = i;            
            for(int j=1;j<=n;j++)//每一位
            {
                if( now%(m+1)==0 )    maxx = max( maxx,dfs(tim-1,a+base,b) );
                now /= (m+1), base *= (m+1);    
            }
            f[a][b] += maxx/m; 
        }
    }
    else
    {
        for(int i=1;i<=m;i++)
        {
            double minn = 1; int now = b, base = i;            
            for(int j=1;j<=n;j++)//每一位
            {
                if( now%(m+1)==0 )    minn = min( minn,dfs(tim-1,a,b+base) );
                now /= (m+1), base *= (m+1);    
            }
            f[a][b] += minn/m; 
        }
    }
    return f[a][b];
}
int main()
{
    cin >> n >> m;
    printf( "%.7lf",dfs(n*2,0,0) );
}

C.CG的通关秘籍

总共的数字方式有,计算每个方案累加显然不可取

经典的贡献法,我们定义每个数字只在作为前面那个数时有贡献

数字只有放在有后继,可能产生贡献,放在这个位置的贡献相等

个数比小,放在后继形成逆序,个数放后继形成顺序

这样的话,数字放在特定位置的贡献就是

所有数字放在这个位置的贡献就是

其实这样相当于固定了数字和它的后继,那么剩下个位置怎么放都有这样的贡献

所以答案为

D.魔物消灭计划

由于拥有同种宝石的城市之间可以直接传送而不消耗任何资源

所以实际上我们可以将拥有同种宝石的城市缩成一个个特殊点

再加上首都和祭坛我们也将它们看成两个特殊点

这样题目实际上就变成了求包含若干特殊点的最小生成树

这实际上是一个最小斯坦纳树的基本模型,套用即可得到解

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

int cnt = 0, head[110],dp[110][1 << 10];;
struct Edge {
    int next, to, w;
} edge[510 << 1];
void init() {
  cnt = 0;
  memset(head, -1, sizeof(head));
  memset(dp, 0x3f, sizeof(dp));
}
void add_edge(int u, int v, int w) {
  edge[++cnt] = {head[u], v, w};
  head[u] = cnt;
}
priority_queue<pair<int,int>, vector<pair<int,int> >, greater<pair<int,int>>> que;
bool vis[110];
void dijkstra(int s) {
  memset(vis, false,sizeof(vis));
  while (!que.empty()) {
    int u = que.top().second;
    que.pop();
    if (vis[u]) continue;
    vis[u] = true;
    for (int i = head[u]; i != -1; i = edge[i].next) {
      int to = edge[i].to;
      if (dp[to][s] > dp[u][s] + edge[i].w) {
        dp[to][s] = dp[u][s] + edge[i].w;
        que.push(make_pair(dp[to][s], to));
      }
    }
  }
}
int n,m,k,x,y,a[110],id[110];
signed main() {
  std::ios::sync_with_stdio(false); cin.tie(0);
  cin >> n >> m >> k >> x >> y;
  id[x] = k + 1,id[y] = k + 2; k += 2;
  int num = k;
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
    if (i == x || i == y) continue;
    if (a[i] == 0) id[i] = ++num;
    else id[i] = a[i];
  }
  n = num; init();
  for (int i = 1; i <= m; i++) {
    int u, v, w;
    cin >> u >> v >> w;
    if (id[u] == id[v]) continue;
    add_edge(id[u], id[v], w);
    add_edge(id[v], id[u], w);
  }
  for (int i = 1; i <= k; i++) dp[i][1 << (i - 1)] = 0;
  for (int s = 1; s < (1 << k); s++) {
    for (int i = 1; i <= n; i++) {
      for (int subs = s & (s - 1); subs; subs = s & (subs - 1)) {
        dp[i][s] = min(dp[i][s], dp[i][subs] + dp[i][s ^ subs]);
      }
      if (dp[i][s] != 0x3f3f3f3f) { que.push(make_pair(dp[i][s], i)); }
    }
    dijkstra(s);
  }
  int ans = 1e9;
  for (int i = 1; i <= n; i++) ans = min(ans, dp[i][(1 << k) - 1]);
  cout << ans << '\n';
  return 0;
}

E.牛牛数数

线性基+(dp|二分)都可

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

struct linearbasis {
    ll base[64], flag, cnt;

    void add(ll x) {
        for(int i = 62; ~i; i--) {
            if(x >> i & 1) {
                if(!base[i]) {
                    base[i] = x;
                    return ;
                }
                x ^= base[i];
            }
        }
        flag = 1;
    }

    void rebuild() {
        cnt = 0;
        for(int i = 62; i >= 0; i--) {
            for(int j = i - 1; j >= 0; j--) {
                if(base[i] >> j & 1) {
                    base[i] ^= base[j];
                }
            }
        }
        for(int i = 0; i <= 62; i++) {
            if(base[i]) {
                ll temp = base[i];
                base[i] = 0;
                base[cnt++] = temp;
            }
        }
    }

    ll query_k(ll k) {
        k -= flag;
        if(k == 0) return 0;
        if(k >= 1ll << cnt) return -1;
        ll ans = 0;
        for(int i = 62; ~i; i--) {
            if(k >> i & 1) {
                ans ^= base[i];
            }
        }
        return ans;
    }
    void init() {
        memset(base, 0, sizeof base), flag = cnt = 0;
    }
}a;

int main() {
//     freopen("9.in", "r", stdin);
//     freopen("9.out", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    ll k, x;
    int n;
    scanf("%d %lld", &n, &k);
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &x);
        a.add(x);
    }
    a.rebuild();
    ll l = 1, r = (1ll << a.cnt) - 1 + a.flag;
    while(l < r) {
        ll mid = l + r >> 1;
        if(a.query_k(mid) <= k) l = mid + 1;
        else r = mid;
    }
    if((l == (1ll << a.cnt) - 1 + a.flag) && a.query_k(l) <= k) l++;
    ll total = (1ll << a.cnt) - 1 + a.flag, now = l;
    printf("%lld\n", total - now + 1);
    return 0;
}

F.phi and phi

图片说明

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10, mod = 1e9 + 7;

int prime[N], phi[N], cnt, n;

ll ans[N];

bool st[N];

void init() {
    phi[1] = 1;
    for(int i = 2; i < N; i++) {
        if(!st[i]) {
            prime[++cnt] = i;
            phi[i] = i - 1;
        }
        for(int j = 1; j <= cnt && 1ll * i * prime[j] < N; j++) {
            st[i * prime[j]] = 1;
            if(i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
    for(int i = 1; i < N; i++) {
        int sum = 0;
        for(int l = i, r; l < N; l += i) {
            sum = (sum + phi[l]) % mod;
            r = l + i; // l + i - 1
            ans[l] = (ans[l] + 1ll * sum * sum % mod * phi[i] % mod) % mod;
            if(r < N) ans[r] = (ans[r] - 1ll * sum * sum % mod * phi[i] % mod + mod) % mod;
        } 
    }
    for(int i = 1; i < N; i++) {
        ans[i] = (ans[i] + ans[i - 1] + mod) % mod;
    }
}

int main() {
    // freopen("in.txt", "r", stdin);
    // freopen("out.txt", "w", stdout);
    // ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    init();
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}
全部评论
E题线性基然后直接暴力就行吧 ```c++ ll c[60], k, a, ans; int cnt[60]; void add(ll a) {     per (i, 59, 0) if (a >> i & 1) {         if (!c[i]) { c[i] = a; break; }         else a ^= c[i];     } } int main() {     IOS; cin >> n >> k;     rep (i, 1, n) cin >> a, add(a);     rep (i, 0, 59) cnt[i] = cnt[i - 1] + (c[i] != 0);     per (i, 59, 1) if (!(k >> i & 1) && c[i])         ans += (1ll << cnt[i - 1]);     if (!(k & 1) && c[0]) ++ans;     cout << ans;     return 0; } ```
2 回复 分享
发布于 2021-01-15 23:37
E题数据确实太水了........
点赞 回复 分享
发布于 2021-01-17 15:19
请问一下E题进行二分时,右边界为啥要加上一个a.flag呀😥,我没加就有一个测试点过不去qwq,直接r=(1LL<<cnt)-1
点赞 回复 分享
发布于 2021-01-17 13:52
E题数据有点水 这样写甚至可以过
点赞 回复 分享
发布于 2021-01-15 22:57
撞题.......我还Wa了几次......
点赞 回复 分享
发布于 2021-01-15 22:16
(而且这个规律之前考数学专题的时候推过一次)
点赞 回复 分享
发布于 2021-01-15 22:10
莫非,就我一个人C题通过打表找规律做的?
点赞 回复 分享
发布于 2021-01-15 22:10

相关推荐

自从我室友在计算机导论课上听说了“刷&nbsp;LeetCode&nbsp;是进入大厂的敲门砖”,整个人就跟走火入魔了一样。他在宿舍门口贴了一张A4纸,上面写着:“正在&nbsp;DP,请勿打扰,否则&nbsp;Time&nbsp;Limit&nbsp;Exceeded。”日记本的扉页被他用黑色水笔加粗描了三遍:“Talk&nbsp;is&nbsp;cheap.&nbsp;Show&nbsp;me&nbsp;the&nbsp;code。”连宿舍聚餐,他都要给我们讲解:“今天的座位安排可以用回溯算法解决,但为了避免栈溢出,我建议用动态规划。来,这是状态转移方程:dp[i][j]&nbsp;代表第&nbsp;i&nbsp;个人坐在第&nbsp;j&nbsp;个位置的最优解。”我让他去楼下取个快递,他不直接去,非要在门口踱步,嘴里念念有词:“这是一个图的遍历问题。从宿舍楼(root)到驿站(target&nbsp;node),我应该用&nbsp;BFS&nbsp;还是&nbsp;DFS?嗯,求最短路径,还是广度优先好。”和同学约好出去开黑,他会提前发消息:“集合点&nbsp;(x,&nbsp;y),我们俩的路径有&nbsp;k&nbsp;个交点,为了最小化时间复杂度,应该在&nbsp;(x/2,&nbsp;y/2)&nbsp;处汇合。”有一次另一个室友低血糖犯了,让他帮忙找颗糖,他居然冷静地分析道:“别急,这是一个查找问题。零食箱是无序数组,暴力查找是&nbsp;O(n)。如果按甜度排序,我就可以用二分查找,时间复杂度降到&nbsp;O(log&nbsp;n)。”他做卫生也要讲究算法效率:“拖地是典型的岛屿问题,要先把连通的污渍区块都清理掉。倒垃圾可以用双指针法,一个指针从左往右,一个从右往左,能最快匹配垃圾分类。”现在我们宿舍的画风已经完全变了,大家不聊游戏和妹子,对话都是这样的:“你&nbsp;Two&nbsp;Sum&nbsp;刷了几遍了?”“别提了,昨天遇到一道&nbsp;Hard&nbsp;题,我连暴力解都想不出来,最后只能看题解。你呢?”“我动态规划还不行,总是找不到最优子结构。今天那道接雨水给我整麻了。”……LeetCode&nbsp;真的害了我室友!!!
老六f:编程嘉豪来了
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
11
6
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务