第五届新疆省ACM-ICPC程序设计竞赛

15 篇文章 0 订阅

A.Good 的集合

三角形重心的公式 : (x=(x_{1}+x_{2}+x_{3})/3,y=(y_{1}+y_{2}+y_{3})/3 )
我们只需要记录每个点坐标 mod 3 的个数,然后我们可以知道这样就只存在 9 种点集 (0-2)*(0-2)
我们可以二进制枚举每种点集是否存在,然后去check。
十分需要注意的一点:同一个点集的点,最多选取两个,如果选取了两个以上是不满足good点集的要求

代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
using namespace std;

#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int n, x, y, sum[3][3];
int judge(int state) {
    int sub = 0, ans = 0;
    vector<pair<int, int> >q;
    for(int i = 0; i <= 2; i++) {
        for(int j = 0; j <= 2; j++) {
            if(state >> sub & 1) {
                q.push_back(make_pair(i, j));
                ans += min(sum[i][j], 2);       ///同一状态的点最多选取两个
            }
            sub++;
        }
    }
    int d = q.size();
    for(int i = 0; i < d; i++) {    ///暴力判断
        for(int j = i + 1; j < d; j++) {
            for(int k = j + 1; k < d; k++) {
                x = q[i].first + q[j].first + q[k].first;
                y = q[i].second + q[j].second + q[k].second;
                if(x % 3 == 0 && y % 3 == 0)
                    return -1;
            }
        }
    }
    return ans;     ///处于state情况下的good点集大小
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; i++) {
        scanf("%d %d", &x, &y);
        sum[x % 3][y % 3]++;    ///根据中心的公式,可得3*3种集合情况
    }
    int ans = 0;
    for(int i = 0; i < (1 << 9); i++) {     ///二进制枚举选取集合的情况
        ans = max(ans, judge(i));
    }
    printf("%d\n", ans);
}

E.斐波那契串

查询的 x,y <=1e18,我们可以知道类fib数列增长的很快,其实不到100项就已经大于1e18了,其实题目上的 x 就是 <=100的。
然后我们可以通过小模拟将查询的第x串第y个字符 不断向前等效跳转,直到跳转到已知串即可得到答案。

代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
using namespace std;

#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const ll mod = 1e9 + 7;
const ll INF = 1e18;
const int maxn = 55;

char a[maxn], b[maxn];
ll fib[205], q, up = 2, x, y;
int main() {
    scanf("%s %s", a + 1, b + 1);
    fib[1] = strlen(a + 1);
    fib[2] = strlen(b + 1);
    ///长度最大为1e18,查询x串可以等效为前面的串
    while(fib[up] < INF) {
        fib[up + 1] = fib[up] + fib[up - 1];
        up++;
    }
    scanf("%d", &q);
    while(q--) {
        scanf("%lld %lld", &x, &y);
        if(x > 2) {     ///如果不是串a和串b
            x = lower_bound(fib + 1, fib + 1 + up, y) - fib;    ///查找到等效的串
            while(x > 2) {      ///直到处理到已知串
                if(y > fib[x - 1]) {
                    y -= fib[x - 1];
                    x -= 2;
                } else {
                    x--;
                }
            }
        }
        if(x == 2)
            printf("%c\n", b[y]);
        else
            printf("%c\n", a[y]);
    }
    return 0;
}

H.虚无的后缀

要求后缀0最多,我们一个数结尾是0,那么一定是 若干个2乘以若个5得来的,0的个数=min(2的个数,5的个数)
转化为多重背包问题 dp[ i ][ j ][ k ] 表示前 i 个数 选 j 个数当有 k 个5 时可得最多2的数量是多少。
我们可以发现可以消去第一维(节省空间)

代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
using namespace std;

#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;

int dp[205][6000];
int x2, x5;
void solve(ll x) {
    x2 = x5 = 0;
    while(x % 2 == 0) {
        x2++;
        x /= 2;
    }
    while(x % 5 == 0) {
        x5++;
        x /= 5;
    }
}
int main() {
    int n, k;
    ll x;
    scanf("%d %d", &n, &k);
    dp[0][0] = 1;
    for(int i = 1; i <= n; i++) {
        scanf("%lld", &x);
        solve(x);
        for(int s = k; s >= 1; s--) {
            for(int e = 29 * n; e >= x5; e--) {
                if(dp[s - 1][e - x5])
                    dp[s][e] = max(dp[s][e], dp[s - 1][e - x5] + x2);
            }
        }
    }
    int ans = 0;
    for(int i = 0; i <= 29 * n; i++) {
        ans = max(ans, min(dp[k][i], i));
    }
    printf("%d\n", ans);
    return 0;
}

J.异或的路径

用了异或的性质 a^a=0。我们可以求到每个点到根所有边的异或值 xor[ i ],每两个点之间路径的异或和就等于xor[ i ]^xor[ j ]。因为LCA(i, j)到根节点的边权出现了两次,会变为0。所以 答案等于\sum_{i=1}^{n}\sum_{j=1}^{n}xor[i]\wedge xor[j]
我们可以通过计算二进制每一位的贡献来得到答案。

代码:

///#include<bits/stdc++.h>
///#include<unordered_map>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
using namespace std;
 
#define lowbit(x) (x&(-x))
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = exp(1.0);
const ll mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
 
struct node {
    int e;
    ll c;
    int p;
} load[maxn];
int head[maxn], sign;
ll Xor[maxn];
ll sum[20][2];
void add_edge(int s, int e, ll c) {
    load[++sign] = node{e, c, head[s]};
    head[s] = sign;
}
void dfs(int s) {
    for(int i = head[s]; ~i; i = load[i].p) {
        Xor[load[i].e] = Xor[s] ^ load[i].c;
        dfs(load[i].e);
    }
}
void init() {
    memset(head, -1, sizeof(head));
    memset(sum, 0, sizeof(sum));
}
int main() {
    init();
    int n, fa;
    ll c, ans = 0;
    scanf("%d", &n);
    for(int i = 2; i <= n; i++) {
        scanf("%d %lld", &fa, &c);
        add_edge(fa, i, c);
    }
    dfs(1);
    for(int i = 1; i <= n; i++) {
        for(int j = 20; j >= 0; j--) {
            sum[j][Xor[i] >> j & 1]++;
        }
    }
    for(int j = 20; j >= 0; j--) {
        ans = (ans + sum[j][1] * sum[j][0] * (1 << j)) % mod;
    }
    printf("%lld\n", ans * 2 % mod);
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值