Codeforces Round #519 by Botan Investments

Codeforces Round #519 by Botan Investments


A. Elections

#include <bits/stdc++.h>
using namespace std;
int n, mx = 0, sum = 0, x;
int main() {
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) 
        scanf("%d",&x), sum += x, mx = max(mx, x);
    int k;
    
    for(k = mx; k <= 500; ++k) {
        if(k*n > (sum<<1)) {
            printf("%d\n",k); return 0;
        }
    }
    return 0;
}

B. Lost Array

做法:枚举k判断合法性,根据前k+1个a,求出x,再进行判断即可。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n, cnt, b[1111];
ll a[1111], x[1111];
int ck(int k) {
    for(int i = 0; i < k; ++i) x[i] = a[i+1] - a[i];
    ll b = 0;
    for(int i = 1; i <= n; ++i) {
        b = b + x[(i-1)%k];
        if(b != a[i]) return 0;
    }
    return 1;
}
int main() {
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) scanf("%lld",&a[i]);
    for(int k = 1; k <= n; ++k) {
        if(ck(k)) b[++cnt] = k;
    }
    printf("%d\n",cnt);
    for(int i = 1; i<= cnt; ++i) printf("%d ",b[i]); puts("");
    return 0;
}

C. Smallest Word

做法:从前到和维护一个前i个字符能构成字典序最小的串,然后对于第i个串,需要考虑前i-1个字符形成的串,以及加入第i个字符后是否反转,转移即可。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n, ans[1111];
char str[1111];
bool operator < (const string a, const string b) {
    for(int i = 0; i < a.size(); ++i) if(a[i] != b[i]) return a[i] < b[i];
    return 1;
}
string cal(string s) {
    string t = s;
    reverse(t.begin(), t.end());
    return min(t,s);
}

int main() {
    scanf("%s", str+1); n = strlen(str+1);
    string s, t1, t2, t11, t22; s += str[1];
    for(int i = 2; i <= n; ++i) {
        t1 = t2 = s;
        t1 += str[i];
        reverse(t2.begin(), t2.end()); t2 += str[i];
        t11 = t1;   reverse(t11.begin(), t11.end());
        t22 = t2;   reverse(t22.begin(), t22.end());    
        s = min(min(t1,t2), min(t11, t22));
        if(s == t1) continue;
        else if(s == t2) ans[i-1]^=1;
        else if(s == t11) ans[i]^=1;
        else ans[i-1]^=1, ans[i]^=1;
    }
    for(int i = 1; i <= n; ++i) printf("%d ",ans[i]); puts("");
    return 0;
}

D. Mysterious Crime

做法:贪心的满足第一个串,然后根据第一个串判断所有的串最长匹配到什么位置,匹配失败后,就将这段区间的数字的答案计算完成了。然后,继续枚举下一段即可,这样已经计算的数字一定是最优的。

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
int n, m, b[100007], pos[12][100007], p[12], a[12][100007];
bool ck() {
    if(b[a[1][p[1]]]) return 0;
    if(p[1] > n) return 0;
    for(int i = 2; i <= m; ++i) {
        if(p[i] > n || a[i][p[i]] != a[i-1][p[i-1]] || b[a[i][p[i]]]) return 0;
    }
    return 1;
}
void cal(int x) {
    int ans = 0;
    for(int i = 1; i <= m; ++i) p[i] = pos[i][x];
    while(ck()) {
        for(int i = 1; i <= m; ++i) ++p[i];
        ++ans;
    }
    for(int j = pos[1][x], t = ans; t ; ++j, --t) b[a[1][j]] = t;
    return;
}
int main() {
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; ++i)
        for(int j= 1; j <= n; ++j) {
            scanf("%d",&a[i][j]);
            pos[i][a[i][j]] = j;
        }

    for(int i = 1; i <= n; ++i) if(!b[a[1][i]]) {
        cal(a[1][i]);
    }
    ll ans = 0;
    for(int j = 1;j <= n; ++j) ans += 1LL*b[j];
    printf("%lld\n",ans);
    return 0;
}
/*
4 2
1 2 4 3
4 3 1 2
*/

E. Train Hard, Win Easy

做法:求\[\sum _{i=1}^n \sum_{j=1,j != i,can[i][j] = 0} ^n min(a[i].x+a[j].y, a[i].y+a[j].x)\]
首先 i,j 之间不能计算的答案可以从最终的答案中减去,那么考虑如何优化掉第二维;min提醒我们可以讨论。当\(a[i].x + a[j].y <= a[i].y + a[j].x\)\(a[i].x - a[i].y <= a[j].x - a[j].y\)时最小值是\(a[i].x + a[j].y\)
排序之后分别考虑贡献即可;另一种情况,同理。可以通过预处理解决。

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = (a); i <= (b); ++i)
#define per(i,a,b) for(int i = (a); i >= (b); --i)
typedef long long ll;
using namespace std;
int n, m, x, y;
struct node{
    ll x, y; int id;
    bool operator < (const node &a) const { return x - y < a.x - a.y; }
}a[300007];
ll ans[300007], sx[300007], sy[300007];
int main() {
    scanf("%d%d",&n,&m);
    rep(i,1,n) scanf("%lld%lld", &a[i].x, &a[i].y), a[i].id = i;
    rep(i,1,m) {
        scanf("%d%d", &x, &y);
        ans[x] -= min(a[x].x+a[y].y, a[x].y+a[y].x);
        ans[y] -= min(a[x].x+a[y].y, a[x].y+a[y].x);
    } sort(a+1, a+1+n);
    per(i,n,1) sy[i] = sy[i+1] + a[i].y; rep(i,1,n) sx[i] = sx[i-1] + a[i].x;
    for(int num, i = 1; i <= n; ++i) {
        num = lower_bound(a+1,a+1+n,a[i])-a-1;
        ans[a[i].id] += a[i].x*(n - num - 1LL) + sy[num+1] - a[i].y + a[i].y*num + sx[num];
    }
    for(int i = 1; i<= n; ++i) printf("%lld ", ans[i]); puts("");
}

F. Make It One

做法:求至少挑多少个数可以使得他们的gcd为1。首先,可以证明最多挑7个数,因为这些数两两之间至少有一个不重复的公共素数。\(dp[i][j]\) 表示选 i 个数gcd为 j 的方案数,那么通过容斥转移就是
\[dp[i][j] = C_{num_j}^i - \sum_{k=2}^\infty dp[i][j*k] \]
\[num_j = \sum_{i=1}^n [a[i] ~mod ~j = 0]\]
即从包含j的数里挑i个的方案数,去掉包含\(j\)的倍数的方案数,这个值可能很大运算过程中取模即可。答案就是找到最小的\(i\)使得\(dp[i][1]>0\)

#include <bits/stdc++.h>
typedef long long ll;
const int N = 3e5 + 7;
const int mod = 1e9 + 7;
using namespace std;
int num[N], a[N], n, MX;
ll dp[11][N], f[N], inv[N];
ll q_pow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b&1) ans = (ans*a) % mod;
        a = (a*a) % mod;
        b >>= 1LL;
    }
    return ans;
}
void init() {
    f[0] = 1;
    for(int i = 1; i <= 3e5; ++i) f[i] = (f[i-1]*i) % mod;
    inv[300000] = q_pow(f[300000], mod-2);
    for(int i = 300000-1; i >= 0; --i) inv[i] = (inv[i+1]*(i+1LL)) % mod;
}
ll C(int n, int m) {
    if(m > n) return 0;
    if(m == n || !m ) return 1;
    return (f[n]*inv[m])%mod*inv[n-m]%mod;
}
void solve(int x) {
    for(int i = 1; i*i <= x; ++i) if(x % i == 0) {
        ++num[i];
        if(i*i == x) continue;
        ++num[x/i];
    }
}
int main() {
    init();
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i) 
        scanf("%d", &a[i]), solve(a[i]), MX = max(MX,a[i]);
    for(int i = 1; i <= 7; ++i) {
        for(int j = MX; j >= 1; --j) {
            dp[i][j] = C(num[j],i);
            for(int k = j+j; k <= MX; k += j) {
                dp[i][j] -= dp[i][k];
                if(dp[i][j] < 0) dp[i][j]+=mod;
                dp[i][j] %= mod;
            }
        }
        if(dp[i][1] > 0) {
            printf("%d\n",i); return 0;
        }
    }
    puts("-1");
    return 0;
}

G. Speckled Band

做法:略猛,有缘再写了。。。

转载于:https://www.cnblogs.com/RRRR-wys/p/9898128.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值