henry-1202

A

做法:模拟
这里给的计算器操作都是可逆的。那么只要注意中间会爆longlong用int128存就好,比赛的时候因为这个东西爆了两发...

#include <bits/stdc++.h>
using namespace std;
namespace io {
 
    #define in(a) a=read()
    #define out(a) write(a)
    #define outn(a) out(a),putchar('\n')
 
    #define I_int __int128
    inline I_int read() {
        I_int x = 0 , f = 1 ; char c = getchar() ;
        while( c < '0' || c > '9' ) { if( c == '-' ) f = -1 ; c = getchar() ; }
        while( c >= '0' && c <= '9' ) { x = x * 10 + c - '0' ; c = getchar() ; }
        return x * f ;
    }
    char F[ 200 ] ;
    inline void write( I_int x ) {
        if( x == 0 ) { putchar( '0' ) ; return ; }
        I_int tmp = x > 0 ? x : -x ;
        if( x < 0 ) putchar( '-' ) ;
        int cnt = 0 ;
        while( tmp > 0 ) {
            F[ cnt ++ ] = tmp % 10 + '0' ;
            tmp /= 10 ;
        }
        while( cnt > 0 ) putchar( F[ -- cnt ] ) ;
    }
    #undef I_int
 
}
using namespace io ;
 
#define N 110
#define ll __int128
int n;
ll x;
ll opt[N];
ll a[N];
 
int main() {
    scanf("%d", &n);
    x = read();
    for(int i = 1; i <= n; ++i) {
        opt[i] = read(), a[i] = read();
    }
    for(int i = n; i; i--) {
        if(opt[i] == 1) x = (__int128)(x - a[i]);
        if(opt[i] == 2) x = (ll)(x + a[i]);
        if(opt[i] == 3) x = (ll)(x / a[i]);
        if(opt[i] == 4) x = (ll)(x * a[i]);
    }
    outn(x);
    return 0;
}

B

做法:贪心
猜的结论是0后面能放4就优先放4,4后面能优先放0就放0,2后面能优先放0就放0,这样就过了,貌似和标算做法不一样,不过应该是对的。

#include <bits/stdc++.h>
using namespace std;
 
#define ll long long
#define N 100010
ll ans = 0;
int n, a[N], cnt[5], f[N];
 
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), cnt[a[i]]++;
    for(int i = 1; i <= n; ++i) {
        if(f[i - 1] == 0) {
            if(cnt[4]) cnt[4]--, ans += 16, f[i] = 4;
            else if(cnt[2]) cnt[2]--, ans += 4, f[i] = 2;
            else if(cnt[0]) cnt[0]--, f[i] = 0;
            continue;
        }
        if(f[i - 1] == 2) {
            if(cnt[0]) cnt[0]--, ans += 4, f[i] = 0;
            else if(cnt[4]) cnt[4]--, ans += 4, f[i] = 4;
            else if(cnt[2]) cnt[2]--, f[i] = 2;
            continue;
        }
        if(f[i - 1] == 4) {
            if(cnt[0]) cnt[0]--, ans += 16, f[i] = 0;
            else if(cnt[2]) cnt[2]--, ans += 4, f[i] = 2;
            else if(cnt[4]) cnt[4]--, f[i] = 4;
        }
    }
    printf("%lld\n", ans);
    return 0;
}

C

做法:dp
赛时想了一个dp然后自行hack掉了于是写D去了。
赛后发现数据太水这个明显错误的dp也可以过。
果然我dp还是菜,完全没想到正解。
将原序列排序一遍,那么每个数可以转移的区间就是以它为起点的后缀,设\(f[i][j]\)表示当前在点i,耐久度为j是否可行,分为走这里和不走这里两种情况转移(注意如果前后两个点价值相同那肯定不走),然后类似01背包那样子转移。
注意dp数组要第二维要开大一点(至少要大于6000),然后用bool存

#include <bits/stdc++.h>
using namespace std;
 
bool f[3010][10010];
int n;
struct Node {
    int id, val;
}a[3010];
 
bool cmp(Node a, Node b) { return a.val > b.val; }
 
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) scanf("%d", &a[i].val), a[i].id = i;
    sort(a + 1, a + n + 1, cmp);
    int l;
    for(int i = 1; i <= n; ++i)
        if(a[i].id == 1) {
            l = i;
            f[i][a[i].val] = 1;
            break;
        }
    for(int i = l + 1; i <= n; ++i) {
        if(a[i].id == n) {
            l = i;
            for(int j = 6010; j >= 0; --j) f[i][j] |= f[i - 1][j ^ a[i].val];
            break;
        }
        if(a[i].val == a[i - 1].val) {
            for(int j = 0; j <= 6010; ++j) f[i][j] |= f[i - 1][j];
        } else {
            for(int j = 0; j <= 6010; ++j) f[i][j] |= f[i - 1][j], f[i][j] |= f[i - 1][j ^ a[i].val];
        }
    }
    for(int i = 6010; i; --i)
        if(f[l][i]) return printf("%d\n", i), 0;
    puts("-1");
}

D

做法:欧拉函数
用容斥也是可以算的但是不会。(一位大佬:这不是入门容斥吗)
\(gcd(x,n)=gcd(n-x,n)\)可知与n互质的数一定是成对出现的,而且两人从两端走所以x和n-x一定同时走到...(赛时就没想到这个),每对互质的数一共会给两人分别带来\(k^{n}\)的收益。也就是说每个人的总收益是\(k^{\frac{\phi(n)}{2}*n}\)(与n互质的数的和为\(\frac{\phi(n)}{2}*n\)),于是直接这样算出来这个值然后乘A+B就行了。比赛时没想到x和n-x一定同时走到于是暴力处理出来每个和它互质的数然后直接算...
其实就是把公式展开了而已,不过也有90分。
关于上面的公式的证明,可以戳这里

#include <bits/stdc++.h>
using namespace std;
   
#define ll long long
#define N 3162278
const ll mod = 1e9 + 7;
ll n, k, A, B, cnt = 0;
   
ll power(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans;
}
   
int main() {
    scanf("%lld%lld%lld%lld", &n, &k, &A, &B);
    ll m = n, phi = n;
    for(ll i = 2; i * i <= m; ++i) {
        if(m % i == 0) {
            while(m % i == 0) m /= i;
            phi = phi / i * (i - 1);
        }
        if(m == 1) break;
    }
    if(m > 1) phi = phi / m * (m - 1);
    printf("%lld\n", ((A + B) % mod) * (power(k % mod, phi / 2 * n) % mod) % mod);
}

90分暴力

#include <bits/stdc++.h>
using namespace std;
    
#define ll long long
#define N 5000100
const ll mod = 1e9 + 7;
ll n, k, A, B, cnt = 0;
ll a[N];
    
ll power(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1) ans = (__int128)ans * a % mod;
        a = (__int128)a * a % mod;
        b >>= 1;
    }
    return ans;
}
 
ll gcd(ll a, ll b) {
    if(!b) return a;
    return gcd(b, a % b);
}
 
int main() {
    scanf("%lld%lld%lld%lld", &n, &k, &A, &B);
    //ll phi = mod - 1;
    cnt = 0;
    for(ll i = 1; i <= n; ++i)
        if(gcd(n, i) == 1) {
            a[++cnt] = i;
//            a[++cnt] = n - i;
//              if(cnt > (int)(1e6)) while(1) puts("233");
        }
    ll a1 = 1, b1 = 1; k %= mod;
    for(ll i = 1; i <= cnt; ++i) {
        a1 = (__int128)a1 * power(k, a[i]) % mod;
//      a1 = (__int128)a1 * power(k, n - a[i]) % mod;
//      b1 = (__int128)b1 * power(k, a[i]) % mod;
        b1 = (__int128)b1 * power(k, n - a[i]) % mod;
    }
    A %= mod; B %= mod;
    A = (__int128)A * a1 % mod; B = (__int128)B * a1 % mod;
    printf("%lld\n", (A % mod + B % mod) % mod);
}

其他题不会了。可能会补一下G题的题解
官方题解戳这里
提取码是k8xd

相关文章: