vandan 了,最后一场了!
A 【模板】分治FFT
考虑只有 \(3\) 堆水果的情况,设有 \(a,b,c\),则按一定顺序合并的答案是 \(a\times b+(a+b)\times c=ab+bc+ac\)。可以发现所有情况答案一样。那我们只需要模拟一次合并再乘以方案数即可。
考虑第一次合并,\(n\) 个数里任选两个,且前后没有顺序,方案数是 \(\binom{n}{2}\)。
然后,把合并好的那一堆看成一个,递归上面的过程,发现每一次合并都会使堆总数减一,那么方案数就是 \(\displaystyle \prod_{i=1}^{n}\binom{i}{2}\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+5,mod=998244353;
int n,a;
inline int getc(int n)
{return n*(n-1)%mod*499122177%mod;
}
signed main()
{freopen("fft.in","r",stdin);freopen("fft.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>a;int ans=0,sum=a;for(int i=2;i<=n;i++){cin>>a;ans=(ans+(sum*a)%mod)%mod; sum=(sum+a)%mod;}for(int i=n;i>1;i--) ans=(ans*getc(i))%mod;cout<<ans;
}
B 【模板】最近公共祖先
考虑是一棵树时的构造,一定是从下往上选,如果遇到 \(siz_u\) 大于 \(1\) 的情况,就让儿子互相连边,如果剩下一个就传给连向 \(u\) 父亲的边。
这样答案就是 \(\lfloor \frac m 2\rfloor\),构造很成立。
然后如果有非树边,传给父亲等着连。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int T;
int n,m;
const int N=3e5+5;
vector<int>e[N];
struct node{int x,y,z;
};
int siz[N],fa[N];
bitset<N>vis;//whether the chain from father to it has been chosen
vector<node>ans{};
int lst[N],ins[N];
queue<int>q;
void dfs(int u)
{vis[u]=ins[u]=1;for(int v:e[u]){if(!vis[v])dfs(v);}for(int v:e[u]){if(ins[v]) continue;if(lst[v]) ans.push_back({u,v,lst[v]}),lst[v]=0;else q.push(v);}while(q.size()>=2){int x=q.front();q.pop();int y=q.front();q.pop();ans.push_back({x,u,y});}if(!q.empty()){int x=q.front();q.pop();lst[u]=x;}ins[u]=0;
}
void Q()
{cin>>n>>m;for(int i=1;i<=m;i++) {int u,v;cin>>u>>v;e[u].push_back(v);e[v].push_back(u);}for(int i=1;i<=n;i++)if(!vis[i])dfs(i);cout<<ans.size()<<"\n";for(node i:ans) cout<<i.x<<" "<<i.y<<" "<<i.z<<"\n";
}
signed main()
{freopen("lca.in","r",stdin);freopen("lca.out","w",stdout);ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);T=1;while(T--)Q();
}
C 【模板】普通平衡树
\(n^2\) 我会。
设 \(dp[i][1/0]\) 表示当前在 \(1\),上一个数小于(大于)\(a_i\) 的最大长度,直接转移即可。
D 【模板】平面最近点对
上场先开 T4,写了俩小时二分答案写假了。。。
最后还不如人家暴力分高。