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;
}

浙公网安备 33010602011771号