Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

更新 #9

Merged
merged 6 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions 3-动态规划.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,44 @@ int main()
}
```

数位dp中常见的状态设置:

以下为记忆化搜索函数dfs的常设定的形参

- pos:int型变量,表示当前枚举的位置,一般从高到低。// pos-1

- limit:bool型变量,表示枚举的第pos位是否受到**限制**,// limit&&(i==a[pos])

- 为true表示取的数不能大于a[pos],而只有在[pos+1,len]的位置上填写的数都等于a[i]时该值才为true
- 否则表示当前位没有限制,可以取到[0,R−1],因为R进制的数中数位最多能取到的就是R−1

- last:int型变量,表示上一位(第pos+1位)填写的值 // i

- 往往用于约束了相邻数位之间的关系的题目

- lead0:bool型变量,表示是否有**前导零**,即在len→(pos+1)这些位置是不是都是**前导零**// lead0&&(i==0)

- 基于常识,我们往往默认一个数没有前导零,也就是最高位不能为0,即不会写为000123,而是写为123

- 只有没有前导零的时候,才能计算0的贡献。

- 那么前导零何时跟答案有关?

- 统计0的出现次数
- 相邻数字的差值
- 以最高位为起点确定的奇偶位

- sum:int型变量,表示当前len→(pos+1)的数位和 // sum+i

- r:int型变量,表示整个数前缀取模某个数m的余数 // (10*r+i)%m

- 该参数一般会用在:约束中出现了能被m整除
- 当然也可以拓展为数位和取模的结果

- st:int型变量,用于状态压缩 // st^(1<<i)

- 对一个集合的数在数位上的出现次数的奇偶性有要求时,其二进制形式就可以表示每个数出现的奇偶性

## 状压DP解哈密顿回路问题

```cpp
Expand Down
210 changes: 210 additions & 0 deletions 4-数学.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,3 +518,213 @@ vector<ll> factorize(ll n)
return p;
}
```

## FFT与NTT
以模板题[P3803 【模板】多项式乘法(FFT)](https://www.luogu.com.cn/problem/P3803)为例

### FFT
```cpp
//使用vector:737ms/1.83s
//使用全局C数组:652ms/1.69s
const double pi=3.14159265358979323846264338;
const ll rmax=3e6;
const ll maxn=rmax+10;
using comp=complex<double>;
comp a[maxn];
comp b[maxn];
ll r[maxn];//这几个东西的范围应该是一个比2e6大的2的某个幂这样子
void change(comp a[],ll n){
rep(i,0,n-1){
r[i]=r[i/2]/2;
if(i&1) r[i]+=n/2;
}
rep(i,0,n-1){
if(i<r[i]) swap(a[i],a[r[i]]);
}
}
void fft(comp a[],ll n,ll op){
change(a,n);
for(ll m=2;m<=n;m<<=1){
comp w1({cos(2*pi/m),sin(2*pi/m)*op});//相同的长度具有相同的基底
for(ll i=0;i<n;i+=m){
comp wk({1,0});
for(ll j=0;j<m/2;j++){
comp x=a[i+j],y=a[i+j+m/2]*wk;
a[i+j]=x+y;
a[i+j+m/2]=x-y;
wk*=w1;
}
}
}
}
void solve(){
ll n,m;
cin>>n>>m;
ll sum=n+m;
ll len=1;
while(len<=sum){
len<<=1;
}
rep(i,0,n){
cin>>a[i];
}
rep(i,0,m){
cin>>b[i];
}
fft(a,len,1);fft(b,len,1);
for(ll i=0;i<len;i++){
a[i]=a[i]*b[i];
}
fft(a,len,-1);
rep(i,0,sum){
cout<<(ll)(a[i].real()/len+0.05)<<" ";
}
cout<<endl;
}
```

### NTT
```cpp
//使用vector:586ms/1.54s
//使用全局C数组:522ms/1.41s
const ll rmax=3e6;
const ll maxn=rmax+10;
const ll mod=998244353;
ll g=3;//原根
ll ginv=332748118;
ll a[maxn];
ll b[maxn];
ll r[maxn];
ll fast(ll b,ll idx){
ll ans=1;
while(idx){
if(idx&1) ans=ans*b%mod;
b=b*b%mod;
idx>>=1;
}
return ans;
}
void change(ll a[],ll n){
rep(i,0,n-1){
r[i]=r[i/2]/2;
if(i&1) r[i]+=n/2;
}
rep(i,0,n-1){
if(i<r[i]) swap(a[i],a[r[i]]);
}
}
void ntt(ll a[],ll n,ll op){
change(a,n);
for(ll m=2;m<=n;m<<=1){
ll w1=(op==1?g:ginv);
w1=fast(w1,(mod-1)/m);
for(ll i=0;i<n;i+=m){
ll wk=1;
for(ll j=0;j<m/2;++j){
ll x=a[i+j],y=a[i+j+m/2];
a[i+j]=(x+y*wk)%mod;
a[i+j+m/2]=(x-y*wk%mod+mod)%mod;
wk=wk*w1%mod;
}
}
}
}
void solve(){
ll n,m;
cin>>n>>m;
ll sum=n+m;
ll len=1;
while(len<=sum){
len<<=1;
}
rep(i,0,n){
cin>>a[i];
}
rep(i,0,m){
cin>>b[i];
}
ntt(a,len,1);ntt(b,len,1);
rep(i,0,len-1){
a[i]=a[i]*b[i]%mod;
}
ntt(a,len,-1);
ll leninv=fast(len,mod-2);
rep(i,0,sum){
cout<<a[i]*leninv%mod<<" ";
}
cout<<endl;
}
```

## 线性基

### 构建
```cpp
ll p[60];
void insert(ll x){
for(ll i=31;i>=0;i--){
if(!(x>>i)) continue;
if(!p[i]){
p[i]=x;
break;
}
x^=p[i];
}
}
bool query(ll x){
for(ll i=31;i>=0;i--){
if((x>>i)&1){
x^=p[i];
}
}
return (x==0);
}
```

### 优雅化
```cpp
void rebuild(){
ll cnt=0;
per(i,31,0){
per(j,i-1,0){
if((p[i]>>j)&1){
p[i]^=p[j];
}
}
if(p[i]){
p1[cnt++]=p[i];
}
}
reverse(p1,p1+cnt);
}
```

### 线性基的交

```cpp
struct node {
ll p[60];
node() {memset(p,0,sizeof p);}
};
node d,all;
node merge(const node&a,const node&b) {
node res;
d=a,all=a; //all表示目前所有可用的低位基
ll k; //k是把Bi和它的低位削减至0所用到的A的异或和
for(int i=0;i<60;++i) {
if(!b.p[i]) continue;
ll v=b.p[i];
k=0;
int j;
for(j=i;j>=0;--j)
if(1LL<<j&v) {
if(all.p[j]>0)
v^=all.p[j],k^=d.p[j];
else break;
}
if(!v) res.p[i]=k;
else all.p[j]=v,d.p[j]=k;
}
return res;
}
```
71 changes: 71 additions & 0 deletions 5-图论.md
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,77 @@ void pathAdd(int u, int v, int x)
```
* 对`u, v`之间简单路径做树上差分的做法是 `tag[u]++, tag[v]++, tag[lca]--, tag[fa[lca]]--`。

## 二分图最大匹配问题
匈牙利算法

用于解决二分图最大匹配问题(如:存在若干男女及其喜欢关系(没有同性恋),求最大匹配数/匹配时人不可重复使用)

dfs解:[P3386 二分图最大匹配 洛谷](https://www.luogu.com.cn/problem/P3386)

```cpp
//男女生模型
vector<ll> edge[maxn];//edge[u]里面有v代表男生u和女生v可以相连
ll vis[maxn];//vis[v],在该次dfs下,女生v是否已经配对
ll match[maxn];//match[v]=u,女生v和男生u配对
bool dfs(ll now){
for(auto t:edge[now]){
if(vis[t]) continue;
vis[t]=1;
if(!match[t]||dfs(match[t])){
match[t]=now;
return true;
}
}
return false;
}
void solve(){
ll n,m,e;
cin>>n>>m>>e;
rep(i,1,e){
ll u,v;
cin>>u>>v;
edge[u].push_back(v);
}
ll ans=0;
rep(i,1,n){
rep(j,1,m){
vis[j]=0;
}
if(dfs(i)) ans++;
}
cout<<ans<<endl;
}
```
这道题比较特殊,两类点是可以在一开始就明确分好的,下面给出一个未知分类的,较为通用的解

```cpp
vector<ll> edge[maxn];
ll vis[maxn];
ll match[maxn];
ll tag;
bool dfs(ll now){
for(auto t:edge[now]){
if(vis[t]==tag) continue;
vis[t]=tag;
if(!match[t]||dfs(match[t])){
match[t]=now;
match[now]=t;
return true;
}
}
return false;
}
ll hungarian(ll n,ll m){
ll ans=0;
rep(i,1,n){
if(match[i]) continue;
tag=i;//类似时间戳
if(dfs(i)) ans++;
}
return ans;
}
```
=======
## 差分约束
给出一组包含m个不等式,n个未知数的不等式组

Expand Down
Loading
Loading