2024noip模拟赛终结篇

vandan 了,最后一场了!

A 【模板】分治FFT

考虑只有 \(3\) 堆水果的情况,设有 \(a,b,c\),则按一定顺序合并的答案是 \(a\times b+(a+b)\times c=ab+bc+ac\)。可以发现所有情况答案一样。那我们只需要模拟一次合并再乘以方案数即可。

考虑第一次合并,\(n\) 个数里任选两个,且前后没有顺序,方案数是 \(\binom{n}{2}\)

然后,把合并好的那一堆看成一个,递推上面的过程,发现每一次合并都会使堆总数减一,那么方案数就是 $ \prod\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\) 的最大长度,直接转移即可。

可以发现当 \(n>2\) 时,最长锯齿子序列长度 \(=2+\) 满足 \((a_i-a_{i-1})\times(a_{i+1}-a_i) <0\)\(i\) 的个数。可以用线段树对每个子段维护左端和右端情况,和 \(i\) 的个数,然后 pushdown 传下去,查到叶子即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+5;
int n,q;
inline int sgn(int x)
{
	return (x>0)?1:-1;
}
struct node{
	int len,ans,cl,cr,wl,wr;
}tr[N<<2];
inline node operator+(node A,node B)
{
	if(!A.len)return B;
	if(!B.len)return A;
	if(A.len==1 && B.len==1)
	{
		int tmp=sgn(B.wl-A.wl);
		return (node){2,2,tmp,tmp,A.wl,B.wl};
	}
	if(A.len==1)
	{
		int tmp=sgn(B.wl-A.wr);
		return (node){1+B.len,B.ans+(tmp!=B.cl),tmp,B.cr,A.wl,B.wr};
	}
	if(B.len==1)
	{
		int tmp=sgn(B.wl-A.wr);
		return (node){1+A.len,A.ans+(tmp!=A.cr),A.cl,tmp,A.wl,B.wr};
	}
	int tmp=sgn(B.wl-A.wr);
	int add=(tmp!=A.cr)+(tmp!=B.cl);
	return (node){A.len+B.len,A.ans+B.ans-(2-add),A.cl,B.cr,A.wl,B.wr};
}
struct SegTree{
	#define lid now<<1
	#define rid now<<1|1
	void pushdown(int now)
	{
		tr[lid]=(tr[lid]+tr[now]),tr[rid]=(tr[rid]+tr[now]);
		tr[now]={0,0,0,0,0,0};
	}
	void modify(int now,int l,int r,int x,int y,node w)
	{
		if(x<=l&&r<=y)
		{
			tr[now]=(tr[now]+w);
			return ;
		}int mid=(l+r)>>1;pushdown(now);
		if(x<=mid) modify(lid,l,mid,x,y,w);
		if(y>mid) modify(rid,mid+1,r,x,y,w);
	}
	int query(int now,int l,int r,int x)
	{
		if(l==r) return tr[now].ans;
		int mid=(l+r)>>1;pushdown(now);
		if(x<=mid) return query(lid,l,mid,x);
		else return query(rid,mid+1,r,x);
	}
}st;

signed main()
{
	freopen("odt.in","r",stdin);
	freopen("odt.out","w",stdout);
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>q;
	while(q--)
	{
		int op;
		cin>>op;
		if(op==1)
		{
			int l,r,x;cin>>l>>r>>x;
			st.modify(1,1,n,l,r,{1,1,0,0,x,x});
		}
		else
		{
			int x;cin>>x;
			cout<<st.query(1,1,n,x)<<"\n";
		}
	}
}

D 【模板】平面最近点对

上场先开 T4,写了俩小时二分答案写假了。。。

最后还不如人家暴力分高。

posted @ 2024-11-28 15:53  ccjjxx  阅读(52)  评论(0)    收藏  举报