MX 暑假集训 8.5
我咋啥都不会。学完各种线代和群论再写。
[NOI Online #3 提高组] 魔法值
题意
给定一张 \(n\) 个节点 \(m\) 条边的无向图,初始时点 \(i\) 的点权为 \(a_i\),每个时刻所有点点权同时变化为相邻所有点点权异或和,\(q\) 次询问,每次询问 \(x\) 个时刻后 \(1\) 号节点点权。
\(1\le n,q\le 100,1\le m\le \frac{n(n-1)}{2},1\le a_i,x< 2^{32}\)。
solution
令 \(f_{i,j}\) 表示点 \(i\) 在 \(j\) 个时刻后的点权,转移显然是线性转移,所以可以用 xor-and 矩阵快速幂优化。
但是如果每次询问都用矩阵快速幂求一遍答案时间复杂度是 \(O(n^3q\log x)\) 的,不可接受。
考虑预处理出 \(2^k(0\le k<32)\) 次的转移矩阵,二进制拆分乘起来即可,由于向量与矩阵相乘是 \(O(n^2)\) 的,所以单次询问可以做到 \(O(n^2\log x)\)。
由于我们最终只需要求出 \(1\) 号节点的点权,于是预处理矩阵和询问时都可以用 bitset 优化,最终时间复杂度 \(O(\frac{n^3\log x}{\omega }+\frac{n^2 q\log x}{\omega})\)。
Code
#include<cstdio>
#include<algorithm>
#include<bitset>
using namespace std;
#define uint unsigned int
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=100+10;
constexpr int K=100;
constexpr int inf=1e9+10;
inline void read(int &x) {
x=0;
char ch=getchar();
while (ch<48) ch=getchar();
while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
inline void readu(uint &x) {
x=0;
char ch=getchar();
while (ch<48) ch=getchar();
while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
struct Matrix {
bitset<K> v[K];
inline Matrix() {f(i,0,99)v[i].reset();}
inline void init() {f(i,0,99)v[i].set(i);}
}T[35];
inline Matrix operator *(const Matrix &x,const Matrix &y) {
Matrix res;
f(i,0,99) for (int k=x.v[i]._Find_first();k!=K;k=x.v[i]._Find_next(k)) res.v[i]^=y.v[k];
return res;
}
inline void init_qpow(Matrix a) {T[0]=a;f(i,1,31) T[i]=T[i-1]*T[i-1];}
inline bitset<K> operator *(const Matrix &a,const bitset<K> &b) {
bitset<K> ret;
f(i,0,99) ret.set(i,(a.v[i]&b).count()&1);
return ret;
}
int main() {
int n,m,q;
static uint a[N];
read(n);read(m);read(q);
f(i,0,n-1) readu(a[i]);
Matrix st;
f(i,1,m) {
int x,y;read(x);read(y);
st.v[x-1].set(y-1);
st.v[y-1].set(x-1);
}
init_qpow(st);
while (q--) {
uint x;readu(x);
bitset<K> tmp;tmp.set(0);
F(i,31,0) if ((x>>i)&1) tmp=T[i]*tmp;
uint ans=0;
for (int i=tmp._Find_first();i!=K;i=tmp._Find_next(i)) ans^=a[i];
printf("%u\n",ans);
}
return 0;
}
Swap Permutation
题意
给定长度为 \(n\) 的排列 \(p\),有 \(m\) 次操作,每次操作会选择两个不同的位置并交换上面元素,定义一个排列的权值 \(f(p)=\sum\limits_{i=1}^{n-1}\left\lvert p_i-p_{i+1}\right\rvert\),求所有不同的操作序列得到的排列的权值和。
\(1\le n,m\le 2\times 10^5\)。
solution
拆贡献好题。
考虑枚举一个阈值 \(x\),令 \(c_i=[p_i>x]\) 为第 \(i\) 个位置的颜色,那么由绝对值的定义不难得到,一个排列的权值 \(f(p)=\sum\limits_{x=1}^{n-1}\sum\limits_{i=1}^{n-1}[c_i\ne c_{i+1}]\)。
发现对于相邻两个位置 \(i,i+1\),它们颜色可能的状态只有 \((0,0),(0,1),(1,1)\) 三种(\((0,1)\) 和 \((1,0)\) 没有本质区别,所以只算一种),那么在 \(m\) 次操作后,它们可能变成 \((0,0),(0,1),(1,1)\) 中的一种,我们只需要统计变成每种有多少种方案,就可以得到这两个位置的贡献即为变成 \((0,1)\) 的方案数,由于不同位置的方案数是独立的,所以总权值就是每组 \((i,i+1)\) 对应变成 \((0,1)\) 的方案数之和。
令 \(f_{i,0/1/2}\) 表示 \(i\) 次操作后变成 \((0,0),(0,1),(1,1)\) 的方案数,转移是线性转移,于是可以构造矩阵进行矩阵快速幂优化转移,对于不同位置,最后的转移矩阵是相同的,所以不需要对于每个位置求出转移矩阵,只需要统计出 \((0,0),(0,1),(1,1)\) 位置的个数,并乘上转移矩阵即可。
枚举 \(x\) 并维护 \((0,0),(0,1),(1,1)\) 位置的个数,每次乘上转移矩阵得到当前 \(x\) 的贡献,最后加起来即为答案,时间复杂度 \(O(k^3n\log m)\),其中 \(k=3\)。
Code
#include<cstdio>
#include<algorithm>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)
constexpr int N=2e5+10;
constexpr int inf=1e9+10;
constexpr int mod=998244353;
inline int ad(int x,int y) {return ((x+y>=mod)?(x+y-mod):(x+y));}
inline void add(int &x,int y) {x=ad(x,y);}
inline void read(int &x) {
x=0;
char ch=getchar();
while (ch<48) ch=getchar();
while (ch>=48) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
}
struct Matrix {
int v[3][3];
inline Matrix() {f(i,0,2)f(j,0,2)v[i][j]=0;}
inline void init() {f(i,0,2)v[i][i]=1;}
};
inline Matrix operator *(const Matrix &x,const Matrix &y) {
Matrix ret;
f(i,0,2) f(j,0,2) {
ll sum=0;
f(k,0,2) sum+=1ll*x.v[i][k]*y.v[k][j];
ret.v[i][j]=sum%mod;
}
return ret;
}
inline Matrix qpow(Matrix a,int b) {
Matrix res;res.init();
while (b) {
if (b&1) res=res*a;
a=a*a;b>>=1;
}
return res;
}
int n,m,sum;
int p[N],mp[N];
inline Matrix solve(int c0,int c1) {
Matrix ret;
ret.v[0][0]=ad(mod,(1ll*sum+(c0-2)*2+1)%mod);
ret.v[1][0]=c0-1;
ret.v[2][0]=0;
ret.v[0][1]=c1*2;
ret.v[1][1]=(1ll*sum+(c0-1)+(c1-1)+1)%mod;
ret.v[2][1]=c0*2;
ret.v[0][2]=0;
ret.v[1][2]=c1-1;
ret.v[2][2]=ad(mod,(1ll*sum+(c1-2)*2+1)%mod);
return ret;
}
int main() {
read(n);read(m);sum=(1ll*(n-2)*(n-3)/2)%mod;
f(i,1,n) read(p[i]),mp[p[i]]=i;
Matrix st;st.v[0][2]=n-1;
ll ans=0;
f(t,1,n-1) {
int pos=mp[t];
if (pos!=1) {
if (p[pos-1]<t) st.v[0][1]--,st.v[0][0]++;
else st.v[0][2]--,st.v[0][1]++;
}
if (pos!=n) {
if (p[pos+1]<t) st.v[0][1]--,st.v[0][0]++;
else st.v[0][2]--,st.v[0][1]++;
}
Matrix now=st*qpow(solve(t,n-t),m);
ans+=now.v[0][1];
}
printf("%lld\n",ans%mod);
return 0;
}

浙公网安备 33010602011771号