2024 平邑一中集训 笔记(下)

Day10

考试

T3

形式化题意,给定 \(n,m\),求\(\sum^n_{i=1} \sum^m_{j=1} \displaystyle \begin{pmatrix}n\\i\\\end{pmatrix}\displaystyle \begin{pmatrix}i\\j\\\end{pmatrix}\)

推式子:

\[\sum^n_{i=1} \sum^m_{j=1} \displaystyle \begin{pmatrix}n\\i\\\end{pmatrix}\displaystyle \begin{pmatrix}i\\j\\\end{pmatrix} \]

\[\Longrightarrow\sum^n_{i=1} \sum^m_{j=1}\displaystyle\begin{pmatrix} n\\j\\\end{pmatrix}\displaystyle \begin{pmatrix}n-j\\i-j\\ \end{pmatrix} \]

\[\Longrightarrow\sum^n_{j=1} \sum^m_{i=1}\displaystyle\begin{pmatrix} n\\i\\\end{pmatrix}\displaystyle \begin{pmatrix} n-i\\j-i\\\end{pmatrix} \]

\[\Longrightarrow\sum^m_{i=1} \displaystyle\begin{pmatrix} n\\i\\\end{pmatrix} \sum^n_{j=1} \displaystyle \begin{pmatrix}n-i\\ j-i\\\end{pmatrix} \]

\[\Longrightarrow\sum^m_{i=1} \displaystyle\begin{pmatrix} n\\i\\\end{pmatrix}\displaystyle \begin{pmatrix} \displaystyle \begin{pmatrix} n-i\\1-i\\\end{pmatrix}+\displaystyle \begin{pmatrix}n-i\\2-i\\ \end{pmatrix}+\dots+\displaystyle \begin{pmatrix} n-i\\n-i\\\end{pmatrix}\end{pmatrix} \]

\[\Longrightarrow\sum^m_{i=1} \displaystyle\begin{pmatrix}n\\i\\ \end{pmatrix}2^{n-i} \]

再根据\(C^{i+1}_n=\frac{n-i}{i+1} \times C^i_n\)递推组合数即可

代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=5e6+5;
const ll mod=1e9+7;
ll n,m;
ll ans=0;
ll f[N];//逆元数组 
ll ksm(ll a,ll b){//快速幂 
	ll ans=1;
	for(;b;b>>=1){
		if(b&1) ans=ans*a%mod;
		a=a*a%mod;
	}
	return ans;
}
void dt(){//逆元递推式 
	f[1]=1;
	for(int i=2;i<=m;i++) f[i]=(mod-mod/i)*f[mod%i]%mod;
}
signed main()
{
	cin>>n>>m;
	dt();
	ll zhs=ksm(2,n-1);//代表2^(n-i) 
	ll now=n;//代表代表C^i_n 
	for(int i=1;i<=m;i++){
		ans=(ans+zhs*now%mod)%mod;//更新答案 
		zhs=zhs*f[2]%mod;//将原来的2^(n-i)变为2^(n-i)*2^(-1)=2^(n-i-1) 
		now=now*(n-i)%mod*f[i+1]%mod;//将原来的C^i_n 变为 C^(i+1)_n ,具体见公式 
	}
	cout<<ans;
	return 0;
}

Day11

考试,最遗憾的一集

  • T1暴力打满如果不动代码能靠暴力水过去95pts的,但是结束前10分钟非得写个什么都不是的正解,95pts->35pts
  • T2写出正解了,但是线下评测文件名写错,100pts->0pts
  • T4暴力能拿40pts的,但是脑残把a[i],a[j]写成了i,j。40pts->0pts
  • 265pts->65pts,狂挂200多分
    rk10->rk30+
  • 我以后再写不确定的代码交上去我就是傻逼

赛后一看全是傻逼题,不调了


Day13

T2

给出一颗树, 求满足以下条件的三元点集个数:
假设点集中有节点 \(a,b,c\) 则以 \(dis(a,b)\)\(dis(b,c)\)\(dis(a,c)\) 为三边长的三角形存在。
\(dis(x,y)\) 表示节点 \(x\)\(y\) 在树上的距离。

  • 注意到当三个点在一条链上时必定不能构成三角形,因此可以先求出总方案数,在求出不合法方案数,再用总方案数-不合法方案数即为答案
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,siz[N],ans=0,cnt=0;
//siz[i]代表以i为根的所有子树大小之和(包括i)
vector<int> tu[N];
void dfs(int x,int lst){
	siz[x]=1;
	for(int i=0;i<tu[x].size();i++){
		int u=tu[x][i];
		if(u!=lst){
			dfs(u,x);
			cnt+=siz[u]*(n-1-siz[u]);//当前子树节点个数*除该子树外所有节点的个数(包括x的另外一些子树)
			siz[x]+=siz[u];//x的子树大小更新
		}
	}
	cnt+=(siz[x]-1)*(n-siz[x]);//由于从外面到里面的方案加了一次,子树内部的总方案数加2次,因此要将总子树内部的点与外界的方案再加一次,最终将结果除二即可
}
signed main()
{
	cin>>n;
	for(int i=1;i<=n-1;i++){
		int u,v;
		cin>>u>>v;
		tu[u].push_back(v);
		tu[v].push_back(u);
	}
	int ans=n*(n-1)*(n-2)/6;
	dfs(1,0);
	cout<<ans-cnt/2;
	return 0;
}

T3

洛谷P7167

  • 注意到喷泉向下留到的第一个圆盘一定是第一个比它大的圆盘,由此考虑单调栈预处理
  • 再采取倍增加快速率,设 \(f[i][j]\) 表示从 \(i\) 节点向下第 \(2^j\) 个的将能留到的圆盘,\(f[i][j]=f[f[i][j-1]][j-1]\) 递推即可
代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,q,d[N],c[N];
int nxt[N][20],qzh[N][20];//qzh[i][j]代表从i向下能跳到的1-j的圆盘的前缀和
stack<int> st;
void prework(){
	for(int j=1;(1<<j)<=n;j++){
		for(int i=1;i+(1<<j)<=n;i++){
			nxt[i][j]=nxt[nxt[i][j-1]][j-1];
			qzh[i][j]=qzh[nxt[i][j-1]][j-1]+qzh[i][j-1];
		}
	}
}
int query(int r,int v){
	if(c[r]>=v) return r;
	v-=c[r];
	for(int i=18;i>=0;i--){
		if(nxt[r][i]!=0&&qzh[r][i]<v){
			v-=qzh[r][i];
			r=nxt[r][i];
		}
	}
	return nxt[r][0];
}
signed main()
{
	cin>>n>>q;
	c[0]=1e10;
	for(int i=1;i<=n;i++){
		cin>>d[i]>>c[i];
	}
	for(int i=n;i>=1;i--){
		while(st.size()&&d[st.top()]<=d[i]) st.pop();
		if(st.size()){
			nxt[i][0]=st.top();
			qzh[i][0]=c[st.top()];
		}
		st.push(i);
	}
	prework();
	for(int i=1;i<=q;i++){
		int r,v;
		cin>>r>>v;
		cout<<query(r,v)<<"\n";
	}
	return 0;
}


Day15

Tarjan

Tarjan求强连通分量

若在图中存在一个点集 \(|S|\) ,对于任意 \((i,j) \in |S|\) ,\(i,j\) 都可以互相到达,则称 \(|S|\) 为图中的一个强连通分量

例题:B3609 强连通分量

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
vector<int> tu[N];

bool vis[N];
int cnt=0;
int dfn[N],low[N];
stack<int> st;

int all=0;
vector<int> ans[N];
vector<int> anss[N];
void tarjan(int x){
	cnt++;
	dfn[x]=low[x]=cnt;
	vis[x]=1;
	st.push(x);
	for(int i=0;i<tu[x].size();i++){
		int v=tu[x][i];
		if(!dfn[v]){
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if(vis[v]){
			low[x]=min(low[x],dfn[v]);
		}
	}
	if(dfn[x]==low[x]){
		int y;
		all++;
		while(x!=y){
			y=st.top();
			vis[y]=0;
			st.pop();
			ans[all].push_back(y);
		}
	}
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;
		cin>>x>>y;
		tu[x].push_back(y);
	}
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			tarjan(i);
		}
	}
	cout<<all<<"\n";
	for(int i=1;i<=all;i++){
		sort(ans[i].begin(),ans[i].end());
		for(int j=0;j<ans[i].size();j++){
			cout<<ans[i][j]<<' ';
		}
	}
	return 0;
}

P1726 上白泽慧音

  • 求出所有强连通分量然后找到最大的输出即可

Tarjan求割点

去掉一个点后,图不再联通,则称该点为割点

代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int n,m;
vector<int> tu[N];
int ans[N],top=0;
int dfn[N],low[N];
int now=0,ro;
void tarjan(int x){
	int topp=0;
	dfn[x]=low[x]=++now;
	for(int i=0;i<tu[x].size();i++){
		int v=tu[x][i];
		if(!dfn[v]){
			tarjan(v);
			low[x]=min(low[x],low[v]);
			if(low[v]>=dfn[x]){
				topp++;
				if(x!=ro||topp>1){
					ans[x]=1;
				}
			}
		}
		else low[x]=min(low[x],dfn[v]);
	}
}
signed main()
{
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		tu[u].push_back(v);
		tu[v].push_back(u);
	}
	memset(ans,0,sizeof(ans));
	for(int i=1;i<=n;i++){
		if(dfn[i]==0){
			ro=i;
			tarjan(i);
		}
	}
	top=0;
	for(int i=1;i<=n;i++) if(ans[i]) top++;
	cout<<top<<'\n';
	for(int i=1;i<=n;i++) if(ans[i]) cout<<i<<' ';
	return 0;
}

例题:
P3469 BLO-Blockade

  • 观察易得,当删除点为普通点时,对答案所做贡献为 \(2 \times (n-1)\)
  • 当删除点为割点时,对于每个连通块,答案为 \(size \times (n-size)\)

P3225 矿场搭建

  • 在割点分割的若干连通快里放两个救援出口,求方案即可
posted @ 2024-08-16 22:01  dienter  阅读(33)  评论(0)    收藏  举报