多校冲刺 NOIP 20211114 模拟 (30)

我是傻B,我是傻B,我是傻B,T2签到题删调试的时候多删了一句有用的,100->30,连样例都没过,我简直就是个脑瘫

还有5天整就要NOIP了,或许这是我学OI的最后5天了,以后再摸到算法竞赛或许就要到大学打ACM了

希望NOIP的时候无怨无悔吧

T1 构造字符串

脑瘫题,直接并查集维护相同的字符,\(bool\)数组维护不能相同的点,然后瞎乱写就行了

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=1005;
inline void chmin(int &x,int &y){if(x>y)swap(x,y);}
struct node{int x,y,z;}p[maxn];
int n,m,bo[maxn][maxn],a[maxn],fa[maxn];
bool cmp(node a,node b){return a.z>b.z;}
bool vis[maxn];
inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y)
{
	int fx=getfa(x),fy=getfa(y);
	if(fx==fy) return ;
	if(fx<fy) fa[fy]=fx;
	else fa[fx]=fy;
}
signed main()
{
	freopen("str.in","r",stdin);
	freopen("str.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		p[i].x=read(); p[i].y=read(); p[i].z=read();
		if(p[i].x==p[i].y&&p[i].z==-1){puts("-1");return 0;}
		chmin(p[i].x,p[i].y);
	}
	for(int i=1;i<=n;i++) fa[i]=i;
	sort(p+1,p+1+m,cmp);
	for(int i=1;i<=m;i++)
	{
		if(p[i].z==0)
		{
			if(getfa(p[i].x)==getfa(p[i].y)) {puts("-1");return 0;}
			else {bo[p[i].x][p[i].y]=-1;}
		}
		else
		{
			for(int j=p[i].x,k=p[i].y;j<=p[i].x+p[i].z-1;j++,k++) {merge(j,k);}
			if(p[i].y+p[i].z<=n) {bo[p[i].x+p[i].z][p[i].y+p[i].z]=-1;}
		}
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<i;j++) if(bo[j][i]==-1)
		if(getfa(i)==getfa(j)){puts("-1");return 0;}
		else bo[min(getfa(i),getfa(j))][max(getfa(i),getfa(j))]=-1;
	}
	for(int i=1;i<=n;i++)
	{
		if(getfa(i)==i)
		{
			memset(vis,0,sizeof(vis));
			for(int j=1;j<i;j++) if(bo[min(getfa(i),getfa(j))][max(getfa(i),getfa(j))]==-1)
			vis[a[j]]=1; for(int j=0;j<i;j++) if(!vis[j]){a[i]=j;break;}
		}
		else a[i]=a[getfa(i)];
	}
	for(int i=1;i<=n;i++) printf("%d ",a[i]);
}

T2 寻宝

也是个脑瘫题,显然可以先用并查集将所有点先缩一下,发现k<=100,所以\(k^2\)跑下\(dfs\)然后\(O(1)\)回答询问就行了

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=50005;
int fa[maxn],n,m,k,q,jb;
inline int getfa(int x){return fa[x]==x?x:fa[x]=getfa(fa[x]);}
inline void merge(int x,int y)
{int fx=getfa(x),fy=getfa(y); if(fx==fy)return; fa[fx]=fy;}
vector<bool> vec[maxn];
inline int id(int x,int y){return (x-1)*m+y;}
char s[maxn];
int pos[maxn],vis[maxn],cnt,bel[maxn];
int head[maxn],dfsx,scc_num,siz[maxn],num;
int stk[maxn],top,du[maxn],bh[maxn];
struct edge{int to,nxt;}e[maxn<<1];
inline void add(int x,int y){e[++num]=(edge){y,head[x]};head[x]=num;du[x]++;du[y]++;}
namespace solve1{
	bool bo[1003][1003],vs[1003];
	inline void dfs(int x)
	{
		vs[bh[x]]=1;
		for(int i=head[x];i;i=e[i].nxt)
		{
			int y=e[i].to;
			if(!vs[bh[y]]) dfs(y);
		}
	}
	inline void solve1()
	{
		for(int i=1;i<=cnt;i++)
		if(du[i]) stk[++top]=i,bh[i]=top;
		for(int i=1;i<=top;i++) 
		{
			memset(vs,0,sizeof(vs));
			dfs(stk[i]);
			for(int j=1;j<=top;j++)
			bo[bh[stk[i]]][j]=vs[j];
		}
		for(int i=1;i<=q;i++)
		{
			int p1=read(),p2=read(),p3=read(),p4=read();
			int x=id(p1,p2),y=id(p3,p4);
			if(getfa(x)==getfa(y)){puts("1");continue;}
			if(bo[bh[bel[getfa(x)]]][bh[bel[getfa(y)]]]){puts("1");continue;}
			puts("0");
		}
	}
}
signed main()
{
	freopen("treasure.in","r",stdin);
	freopen("treasure.out","w",stdout);
	n=read();m=read();k=read();q=read();
	for(int i=1;i<=n*m;i++) fa[i]=i;
	for(int i=0;i<=m+2;i++) vec[0].push_back(1),vec[n+1].push_back(1);
	for(int i=1;i<=n;i++)
	{
		scanf("%s",s+1);
		vec[i].push_back(1);
		for(int j=1;j<=m;j++)
		if(s[j]=='#') vec[i].push_back(1);
		else vec[i].push_back(0);
		vec[i].push_back(1);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		if(vec[i][j]==0)
		{
			if(j!=m) if(vec[i][j+1]==0) merge(id(i,j),id(i,j+1)),assert(getfa(id(i,j))==getfa(id(i,j+1)));
			if(j!=1) if(vec[i][j-1]==0) merge(id(i,j),id(i,j-1)),assert(getfa(id(i,j))==getfa(id(i,j-1)));
			if(i!=n) if(vec[i+1][j]==0) merge(id(i,j),id(i+1,j)),assert(getfa(id(i,j))==getfa(id(i+1,j)));
			if(i!=1) if(vec[i-1][j]==0) merge(id(i,j),id(i-1,j)),assert(getfa(id(i-1,j))==getfa(id(i,j)));
 		}
	}
	for(int i=1;i<=n*m;i++) if(getfa(i)==i) pos[++cnt]=i,bel[i]=cnt;
	for(int i=1;i<=k;i++)
	{
		int p1=read(),p2=read(),p3=read(),p4=read();
		int x=bel[getfa(id(p1,p2))],y=bel[getfa(id(p3,p4))];
		if(x!=y) add(x,y);
	}
	solve1::solve1();
}

T3 序列

考到知识盲区了,上次打李超树是什么时候来着??全都忘了,以后还是要把所有学过的板子全部熟练掌握才好

不过或许没有以后了

首先比较容易发现这是一个和斜率有关的题目,那么我们将题目转化一下

首先答案显然可以转化成跨过\(p_i\)的区间容易转化为:以\(p_i-1\)为右端点的最优+以\(p_{i}\)为左端点的最优

那么再转化一下就是右端点在\(p_{i}\)右边,左端点为1的最大区间,减去右端点在\(p_{i}\)左边,右端点为1的最小值

那么可以先把答案离线下来,由于k只有2e6种取值,直接李超树维护一下最值就行了

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=2e6+5,maxm=1e6+1;
int a[maxn],b[maxn],n,m;
struct query{int p,k,id;}p[maxn];
int ans[maxn];
bool cmp(query a,query b){return a.p<b.p;}
struct node{int k,b;};
struct tree{
	#define lid id<<1
	#define rid id<<1|1
	node tr[maxn<<2];
	inline void init(){for(int i=1;i<=(maxn<<1);i++)tr[i].b=-10000000000000000ll;}
	inline int calc(node num,int x){return num.k*x+num.b;}
	inline bool cover(node a,node b,int x){return calc(a,x)<=calc(b,x);}
	inline void insert(int id,int l,int r,node num)
	{
		if(cover(tr[id],num,l-maxm)&&cover(tr[id],num,r-maxm)){return tr[id]=num,void();}
		if(l==r) return ;int mid=(l+r)>>1;
		if(cover(tr[id],num,mid-maxm)) swap(tr[id],num);
		if(cover(tr[id],num,l-maxm)) insert(lid,l,mid,num);
		if(cover(tr[id],num,r-maxm)) insert(rid,mid+1,r,num);
	}
	inline int query(int id,int l,int r,int x)
	{
		int mid=(l+r)>>1,ans=-10000000000000000ll;
		if(x<mid) ans=max(ans,query(lid,l,mid,x));
		if(x>mid) ans=max(ans,query(rid,mid+1,r,x));
		ans=max(ans,calc(tr[id],x-maxm));
		return ans;
	}
}T[2];
signed main()
{
	freopen("seq.in","r",stdin);
	freopen("seq.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) a[i]=read()+a[i-1],b[i]=read()+b[i-1];
	for(int i=1;i<=m;i++) p[i].p=read(),p[i].k=read(),p[i].id=i;
	sort(p+1,p+1+m,cmp); int tmp=1; T[0].init();
	for(int i=1;i<=m;i++)
	{
		while(tmp<p[i].p)
		{T[1].insert(1,1,maxm<<1,(node){b[tmp],-a[tmp]});tmp++;}
		int tmp=T[1].query(1,1,maxm<<1,p[i].k+maxm);
		ans[p[i].id]+=tmp;
	}
	tmp=n;
	for(int i=m;i>=1;i--)
	{
		while(tmp>=p[i].p)
		{T[0].insert(1,1,maxm<<1,(node){-b[tmp],a[tmp]});tmp--;}
		ans[p[i].id]+=T[0].query(1,1,maxm<<1,p[i].k+maxm);
	}
	for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
}

T4 构树

超级神仙题目,虽然有2,30分是白送的,不过没有兴趣拿

首先可以直接枚举所有的树,一共有\(n^{n-2}\)种,这个的话看枚举方法了,可以拿20~30分不等

然后还有一个状压做法:

假装我们的树是一颗以1为根的有根树,模拟展开dfs子树关系的过程

\(dp_{i,S,j}\)表示以\(i\)为根,子树里已经已经有\(S\)集合的节点,有\(j\)条边相同

然后为一个\(dp_{i,S,j}\)枚举一个儿子\(v\)以及其子树\(T\)进行合并

复杂度是\(O(3^nn^w)\)

神仙赵\(sir\)提供了一种复杂度为\(O(\sum\limits_{i=1}^{i<=n}\binom{n}{i}*(n-i)^3)\)的吊做法

不过需要矩阵树定理和行列式,我不会所以咕了。。。

下面进入正题

首先\(Cayley\)公式指出一棵无根树最多有\(n^{n-2}\)种形态,证明的话就是\(prufer\)序列最多有\(n^{n-2}\)

扩展\(Cayley\)公式:被确定边分为大小为\(a_1,a_2,\cdots, a_m\)的连通块,则有\(n^{m-2}\prod {a_i}\)种生成树

知道这两个公式之后就可以拓展不少的思路了

首先我们设\(dp_{i,j,k}\)表示在\(i\)的联通块大小为\(j\),至少有\(k\)条边强制不选的方案数

那么转移的话就是枚举一个儿子\(v\)

这是强制不选那条连接\(i,v\)的边

\(z*dp_{i,j,k}*dp_{v,z,l}-dp_{i,j,k+l}\)

这是强制选的

\(dp_{i,j,k}*dp_{v,z,l}-dp_{i,j+z,k+l-1}\)

最后统一乘上那一堆\(n\),然后再用二项式反演容斥一下就出了

不过无法通过,考虑对这个过程进行优化

优化的话就是一个听说比较经典的一个优化,就是在中间转移的时候乘的那个系数可以转化为在\(siz\)中选择一个点,

那么可以用\(0,1\)表示有选择那个点和没有选择那个点进行转移,然后就\(O(n^2)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int maxn=8005,mod=1e9+7;
int jc[maxn],inv[maxn],n,siz[maxn],tmp[2][maxn],ans[maxn];
vector<int>vec[maxn],dp[maxn][2];
inline int C(int x,int y)
{
	return jc[x]*inv[y]%mod*inv[x-y]%mod;
}
inline int ksm(int x,int y)
{
	int res=1;x=x%mod;
	for(;y;y>>=1){if(y&1)res=res*x%mod;x=x*x%mod;}
	return res;
}
inline void init()
{
	jc[0]=inv[0]=1;
	for(int i=1;i<=n;i++) jc[i]=jc[i-1]*i%mod,inv[i]=ksm(jc[i],mod-2);
}
inline void dfs(int x,int f)
{
	siz[x]=1;dp[x][0].resize(2);dp[x][1].resize(2);
	dp[x][0][1]=dp[x][1][1]=1;
	for(auto v:vec[x])if(v!=f)
	{
		dfs(v,x);
		for(int i=0;i<=siz[x]+siz[v];i++) tmp[0][i]=tmp[1][i]=0;
		for(int i=1;i<=siz[x];i++) for(int j=1;j<=siz[v];j++)
		{
			tmp[0][i+j]=(tmp[0][i+j]+dp[x][0][i]*dp[v][1][j])%mod;
			tmp[1][i+j]=(tmp[1][i+j]+dp[x][1][i]*dp[v][1][j])%mod;
			tmp[0][i+j-1]=(tmp[0][i+j-1]+dp[x][0][i]*dp[v][0][j])%mod;
			tmp[1][i+j-1]=(tmp[1][i+j-1]+dp[x][0][i]*dp[v][1][j]+dp[x][1][i]*dp[v][0][j])%mod;
		}
		siz[x]+=siz[v];dp[x][0].resize(siz[x]+1);dp[x][1].resize(siz[x]+1);
		dp[v][0].clear();dp[v][1].clear();
		for(int i=0;i<=siz[x];i++) dp[x][0][i]=tmp[0][i],dp[x][1][i]=tmp[1][i];
	}
}
signed main()
{
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	n=read();init();
	for(int i=1;i<n;i++)
	{	
		int x=read(),y=read();
		vec[x].push_back(y);
		vec[y].push_back(x);
	}
	dfs(1,0);int base=1;ans[n-1]=1;
	for(int i=n-2;~i;i--,base=base*n%mod) ans[i]=dp[1][1][n-i]*base%mod;
	for(int i=0;i<n;i++)
	{
		int res=0;
		for(int j=i,base=1;j<n;j++,base*=-1) res=(res+base*ans[j]*C(j,i)%mod);
		printf("%lld ",(res%mod+mod)%mod);
	} 
}
posted on 2021-11-14 20:49  JYFHYX  阅读(48)  评论(0编辑  收藏  举报