In mathematics you don't understand things, you just get used to them.

标签 group theory 下的文章

poj 2888

n 种轮换($|G|=n$)

第 i 种可以分成长度为 $\frac{n}{\gcd(n,i)}$ 的 $\gcd(n,i)$ 个轮换

$\forall g\in G$,$|X^g|$(不定点个数)等于规模为 $\gcd(n,i)$ 的环的答案然后搞一搞

傻逼poj

P4916

枚举 $d$

转化成有 $d$ 个球,要染 $\frac md$ 个,不能有连续 $k$ 个被染色

考虑插板,有 $\frac{m}{d}$ 个色球,$d-\frac{m}{d}$ 个无色球,把色球插入无色球之间

断换成链的手段是枚举首尾之间插入的色球数量

$\displaystyle \sum_{i=0}^k(i+1)\times f(\frac{m}{d}-i,\frac nd-\frac{m}{d}-1,k)$

$f(n,m,k)$ 表示把 $n$ 个球分成 $m$ 组,每组不超过 $k$ 个的方案数(容斥)

p4128

把点置换转化成边置换,再用 Polya(大体思路)

设点置换群 $\lang G,\cdot \rang$,取某个 $g \in G$,$g=\prod_i (a_{i,1},a_{i,2},...,a_{i,n_i})$

  • $\forall i\neq j$,研究 $(a_i)$ 和 $(a_j)$

    比如 $(u,v)$,会被置换成 $(u+1,v+1)$,$(u+2,v+2)$,直到 $(u+\mathrm{lcm}(n,m),v+\mathrm{lcm}(n,m))$。置换的长度就是两个环的长度的 $\mathrm{lcm}$,一共有 $n\times m$ 个元素,所以一共 $\frac{nm}{\mathrm{lcm}(n,m)}=\gcd(n,m)$ 个循环

  • 研究 $(a_i)$(同一个循环节里面)

    $\frac n2$ 个循环

设 $g$ 拆成 $k$ 个循环

边循环个数 $\sum_{i}^k \frac{n_i}2+\sum_{i=1}^{k-1}\sum_{j=i+1}^k \gcd(n_i,n_j)$

不能枚举 $n!$ 种点排列

发现如果 $\{n_k\}$ 相同,贡献相同

$\{n_k\}$ 加起来是 $n$,枚举 $n$ 的分拆数(妙)

算 $\{n_k\}$ 的贡献系数:钦定 $n_1 \leqslant n_2 \leqslant \dots \leqslant n_k$,把每个循环看作圆排列,设 $t_i$ 为长度为 $i$ 的循环数,则系数为 $\frac{n!}{\prod n_i \prod t_i!}$

dfs+Polya 计算

一个被国内 oi 环境弱化至几乎不存在的概念,不过我觉得还是有学一学的必要。因为我没学过代数结构所以大部分内容都在开黄腔,欲喷从轻。

Semigroup 的定义是,$\forall a,b\in\mathbb{S}$,有 $a\cdot b\in\mathbb{S}$ 并且 $(a\cdot b)\cdot c=a\cdot(b\cdot c)$,则称 $\lang\mathbb{S},\cdot\rang$ 为一个 semigroup,称其中的二元运算 $\cdot:\mathbb{S}\cdot\mathbb{S}\rightarrow\mathbb{S}$ 为半群的乘法。

Semigroup 与 group 的区别在于有无幺(identity element),并且所有元素均有逆。

Monoid 则是在 semigroup 的基础上添加了一个幺,翻译过来即幺半群。

有了这些理论支撑,我们在解决一系列数据结构问题时的思维将得到几乎为零的简化

「ARC 124A」LR Constraints

Link.

我们可以把 $1\sim n$ 个盒子里能放的球的编号集合全部求出来。然后就直接来。

注意题目已经给出了 $k$ 个球的位置,所以「Note that for each integer $i$ from $1$ through $K$, there must be at least one card on which we write $i$.」这个限制不用管。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define int ll
#define len(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
const int N=1100,MOD=998244353;
int n,k,ts[N],tek[N],fin[N],Rs[N];
set<int> rs[N];
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    cin>>n>>k,memset(fin,-1,sizeof fin);
    for(int i=1; i<=k; ++i) {
        char c; cin>>c;
        ts[i]=(c=='R');
        cin>>tek[i];
        Rs[tek[i]]=ts[i];
    }
    for(int i=1; i<=k; ++i) {
        if(~fin[tek[i]]) return puts("0"),0;
        fin[tek[i]]=i;
    }
    for(int i=1; i<=n; ++i) {
        if(~fin[i]) rs[i].emplace(fin[tek[i]]);
        else {
            auto &s=rs[i];
            for(int j=1; j<=k; ++j) s.emplace(j);
            int tmp=0;
            for(int j=i+1; j<=n; ++j) {
                if(!Rs[j]) s.erase(fin[j]);
            }
            for(int j=1; j<i; ++j) {
                if(Rs[j]) s.erase(fin[j]);
            }
        }
    }
    int res=1;
    for(int i=1; i<=n; ++i) res*=len(rs[i]),res%=MOD;
    cout<<res<<"\n";
    return 0;
}

「ARC 124B」XOR Matching 2

Link.

预处理出 $s(i,j)=a_{i}\oplus b_{j}$,以及 $pos(i,x)$ 表示第 $i$ 层值 $x$ 的出现集合,用 std::map 均摊 $\mathcal{O}(n^{2})$ 空间。然后我们在第一层逐列考虑,对于第一层的每一种异或值,找出在每一行出现的位置集合,如果某一行没有出现就不行。最后就看集合大小是否等于 $n$。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()
#define int ll
const int N=2100;
int a[N],b[N],xr[N][N],n;
multimap<int,int> mp[N];
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    cin>>n;
    for(int i=1; i<=n; ++i) cin>>a[i];
    for(int i=1; i<=n; ++i) cin>>b[i];
    for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) xr[i][j]=(a[i] xor b[j]),mp[i].insert({xr[i][j],j});
    vector<int> res;
    for(int j=1; j<=n; ++j) {
        bool ok=0;
        set<int> S;
        for(int i=1; i<=n; ++i) {
            auto rg=mp[i].equal_range(xr[1][j]);
            if(mp[i].find(xr[1][j])!=mp[i].end()) {
                for(auto it=rg.first; it!=rg.second; ++it) {
                    S.emplace(it->second);
                }
            }
            else {
                ok=1;
                break;
            }
        }
        if(ok) continue;
        if(S.size()==n) {
            res.push_back(xr[1][j]);
        }
    }
    sort(all(res));
    res.erase(unique(all(res)),res.end()); 
    cout<<res.size()<<"\n";
    for(int x:res) cout<<x<<"\n";
    return 0;
}

「ARC 124C」LCM of GCDs

Link.

考场做法复杂度有问题啊,虽然屮过去了。有空来补 official editorial 做法。

// Oops, something went wrong.

「ARC 124D」Yet Another Sorting Problem

Link.

你看 ARC 又考置换群了。震撼围观吴老师精确押题。

套路题,连边 $i\rightarrow p_{i}$ 后一堆环摆出来。答案是

$$ n+m-(\text{the number of cycles in the graph})+\\ 2\times\max\{\text{the number of cycles of size of 2 or greater consisting of vertice numbered through }1\text{ to }n,\\ \text{the number of cycles of size of 2 or greater consisting of vertice numbered through }n+1\text{ to }n+m\} $$

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define all(x) (x).begin(),(x).end()
const int N=200100;
int n,m,p[N],vis[N];
signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr),cout.tie(nullptr);
    cin>>n>>m; int x0=0,x1=0,res=n+m,ls=0;
    for(int i=1; i<=n+m; ++i) cin>>p[i];
    for(int i=1; i<=n+m; ++i) {
        if(vis[i]) continue;
        int now=i,len=0,qwq=0,qaq=0;
        while(!vis[now]) {
            ++len;
            if(now<=n) qwq=1;
            else qaq=1;
            vis[now]=1;
            now=p[now];
        }
        if(!qaq&&len>=2) ++x0;
        if(!qwq&&len>=2) ++x1;
        --res;
    }
    cout<<res+2*max(x0,x1)<<"\n";
    return 0;
}

「ARC 124E」Pass to Next

Link.

「ARC 124F」Chance Meeting

Link.