I - Divisibility(思维)

题意:

有命题:将 b 进制数y按位相加,循环无穷次,最终结果若%x == 0,则有y % x == 0,反之不然

给出b和x,判断命题是否成立

……打表发现的b % x == 1时成立,看到有数论大佬推出来的%%%

想看推导的右转https://www.cnblogs.com/lipoicyclic/p/13449188.html

#include<bits/stdc++.h>
using namespace std;
typedef unsigned long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
const int N = 25;

int main() {
    int t;
    ll n, x;
    scanf("%d", &t);
    while(t--) {
        scanf("%lld%lld", &n, &x);
        if(n % x == 1)
            printf("T\n");
        else
            printf("F\n");
    }
    return 0;
}

F - A Very Easy Graph Problem(最小生成树 + 树)

题意:

n个点m条边,第 i 条边的长度为2 ^ i,d(i, j) 表示 i、j 之间的最短路,[....]叫艾佛森括号,当括号内条件成立时为1,否则为0,求:

思路:

可以发现边的长度是递增的,并且任意一条边大于它之前所有边之和,所以构造一个最小的连通图,也就是树,树上两点距离也就是最短路了。每条边的贡献 = 该边左边的1节点数 * 右边的0节点数 * 边权 + 左边的0节点数 * 右边的1节点数 * 边权

参考:https://blog.csdn.net/hddddh/article/details/107849596

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int mod = 1e9 + 7;
const double PI = acos(-1.0);
const int N = 1e5 + 7;

struct node
{
    int v, next, w;
}edge[4 * N];

ll qpow(ll a, ll b) {
    ll ans = 1;
    while(b) {
        if(b & 1)
            ans = ans * a % mod;
        a = a * a % mod;
        b >>= 1;
    }
    return ans % mod;
}

int fa[N], n, m, head[N], tot, cn1, cn0, vis[N];
ll dp[N][2], ans;

void init() {
    ans = 0, tot = 0, cn1 = 0, cn0 = 0;
    for(int i = 1; i <= n; i++) {
        fa[i] = i;
        head[i] = -1;
        dp[i][0] = dp[i][1] = 0;
    }
}

void add(int u, int v, int w) {
    edge[tot].v = v;
    edge[tot].w = w;
    edge[tot].next = head[u];
    head[u] = tot++;
}

int Find(int x) {
    if(fa[x] != x) fa[x] = Find(fa[x]);
    return fa[x];
}

void Union(int x, int y) {
    int tmpx = Find(x);
    int tmpy = Find(y);
    if(tmpx != tmpy)
        fa[tmpx] = tmpy;
}

void dfs1(int u, int pre) {
    if(vis[u] == 1) dp[u][1] = 1;
    if(vis[u] == 0) dp[u][0] = 1;
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].v;
        if(v == pre) continue;
        dfs1(v, u);
        dp[u][0] += dp[v][0];
        dp[u][1] += dp[v][1];
    }
}

void dfs2(int u, int pre) {
    for(int i = head[u]; ~i; i = edge[i].next) {
        int v = edge[i].v;
        int w = edge[i].w;
        if(v == pre) continue;
        dfs2(v, u);
        ans = (ans + dp[v][0] * (cn1 - dp[v][1]) % mod * qpow(2, w)) % mod;//点v一侧的0点数和另一侧的1点数
        ans = (ans + dp[v][1] * (cn0 - dp[v][0]) % mod * qpow(2, w)) % mod;
    }
}

int main() {
    int t, u, v;
    scanf("%d", &t);
    while(t--) {
        scanf("%d%d", &n, &m);
        init();
        for(int i = 1; i <= n; ++i) {
            scanf("%d", &vis[i]);
            if(vis[i] == 0) cn0++;
            else cn1++;
        }
        for(int i = 1; i <= m; ++i) {
            scanf("%d%d", &u, &v);
            if(Find(u) == Find(v)) continue;
            Union(u, v);
            add(u, v, i);
            add(v, u, i);
        }
        dfs1(1, -1);
        dfs2(1, -1);
        printf("%lld\n", ans);
    }
    return 0;
}