yuwj  

D

题意:
边权图,1~n路径最小抑或和
思路:
纯暴搜

代码:

/*
暴搜,因为不能经过重复点,所以需要vis
然后回溯继续搜
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 10 + 5;
int h[N], cnt, n, m;
bool vis[N];

struct EDGE
{
    int to, nxt;
    ll w;
}e[1000];

void add_edge(int from, int to, ll val){
    //cout << "add" << '\n';
    e[++cnt].to = to;
    e[cnt].w = val;
    e[cnt].nxt = h[from];
    h[from] = cnt;
}

ll ans=-1;

void dfs(int u, ll val)
{
    if(n == u){
        if(ans == -1) ans = val;
        else ans = min(ans,val);
        return;
    }

    for(int i = h[u]; i; i = e[i].nxt)
    {
        int v = e[i].to; ll w = e[i].w;
        if(!vis[v])
        {
            vis[v] = true;
            dfs(v,val^w);
            vis[v] = false;
        }
    }
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v; ll w; 
        cin >> u >> v >> w;
        add_edge(u,v,w);
        add_edge(v,u,w);
    }

    vis[1] = 1;
    dfs(1,0);
    cout << ans << '\n';
    return 0;
}

E

题意:
根据Axi ^ Ayi = Zi 构建权值最小的数组A,如果不存在则puts(-1)

思路:
其实是给你点编号和边权,让你找到合法的最小点权和的一张图

1.考虑不存在
存在矛盾抑或和,则不存在,画图发现,多条路径到达一个点,则边权抑或和必须为0,否则-1
换句话说,就是存在环,环上的所有边抑或和为0
然后就能构建出连通块

2.考虑最小值
注意,每个连通块一个点权确定,其他点权唯一确定(路径抑或)
所以只要找一个根节点权值最小,那么整个连通块权值最小
实现细节:维护抑或路径和就行,拿到根权值,去沿着路径更新所有其他结点的权值
如何取到最小?按位贪心
每一位上肯定1的数量越少越好,权值由根节点唯一决定,所以,根节点取0/1时,整棵树在该位的最少1的数量就是最优构造解

代码:

/*
树上遍历fa
图上遍历vis
vector存图,新的dfs就vis
*/
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
vector<pair<int,int>> g[N];
int val[N], n, m, ans[N];
bool vis[N];
vector<int> nw;

void dfs(int u)
{
    vis[u] = 1; nw.push_back(u);
    for(auto [v, w] : g[u])
    {
        //环
        if(vis[v])
        {
            if(val[v]!=(val[u]^w))
            {
                puts("-1");
                exit(0);
            }
            continue;
        }
        val[v] = val[u] ^ w; //所有邻边更新路径和
        dfs(v);
    }
}

int main()
{
    int n, m; cin >> n >> m;
    for(int i = 0; i < m; i++)
    {
        int u, v, w; cin >> u >> v >> w;
        g[u].emplace_back(v,w);
        g[v].emplace_back(u,w);
    }

    for(int i = 1; i <= n; i++)
    {
        // 查看每个连通块的逻辑:遍历所有点,某个点没有vis,那就生成新的连通块
        if(vis[i]) continue;
        dfs(i);
        //给连通块中所有点赋值
        int rt = 0;
        //查看每一位的01个数
        for(int j = 30; j>=0;j--)
        {
            int cnt0 = 0, cnt1 = 0;
            for(auto u : nw)
            {
                if((val[u]>>j) & 1) cnt1++;
                else cnt0++;
            }
            //1的个数更多的时候,用根结点去取反,^,设置根节点该位为1
            if(cnt0 < cnt1) rt |= (1 << j);
        }
        
        for(auto u : nw){
            //用根节点更新连通块里所有结点的值
            ans[u] = val[u]^rt;
        }
        nw.clear(); //清空找下一个连通块
    }

    for(int i = 1; i <= n; i++) printf("%d ", ans[i]);
    puts("");
    return 0;
}

F

题意:
更新A数组,B数组为Ai+k % M,求对于每个k中B的逆序对个数

思路:
注意到,在更新的过程中,会有k-1的数字变成0,逆序对个数改变
但是如果没有数字变成0,逆序对个数不变
所以,如果知道每个点的逆序对个数,我们就可以直接o(1)统计在什么时候哪些点可以直接变0更新了

怎么算呢?
假设这个变化前k-1的位置为p,
那么cnt = - (n - p - 后面的不是k-1的数量)(原来的逆序对数量) + p-1 - 前面的k-1的数量(前面比0大,新的逆序对)

代码:

#include <bits/stdc++.h>
using namespace std;
#define lowbit(x) (x & -x)
const int N = 2e5 + 10;
int n, m, t[N], a[N];
vector<int> V[N];

//一直往后面跑就完了,值域树状数组,统计逆序对数量
void add(int x, int val) {while(x<=m)t[x]+=val, x += lowbit(x);}
int getsum(int x) {int ret = 0;while(x) ret+=t[x], x -= lowbit(x);return ret;}

int main()
{
    cin >> n >> m;
    long long ans = 0;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        V[m - a[i]].push_back(i);
        ans += getsum(m-a[i]-1);
        add(m-a[i],1);
    }
    cout << ans << '\n';
    for(int k = 1; k < m; k++)
    {
        for(int i = 0, l = V[k].size(); i < l; i++)
        {
            int p = V[k][i];
            ans -= n - p - (l-1-i);
            ans += p-1-i;
        }
        cout << ans << '\n';
    }
    return 0;
}
posted on 2025-05-16 09:15  xiaowang524  阅读(12)  评论(0)    收藏  举报