ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NOIP模拟97(多校30)

2021-11-14 19:34:51  阅读:139  来源: 互联网

标签:return NOIP int 30 tre long find 97 define


T1 构造字符串

解题思路

不算特别难的题,但是有一点细节。。。

首先需要并茶几缩一下点,然后判断一下是否合法,由于我们需要字典序最小的,因此我们应当保证一个联通块中标号较小的点为根节点。

那么对于所有不能够相等的标号对,我们再标号较大的点记下来标号较小的点的限制,然后从前往后扫一遍取 \(mex\) 值就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e3+10;
int n,m,fa[N],ans[N];
struct Node{int x,y,z;}s[N];
vector<int> v[N];
bitset<N> bit;
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
#undef int
int main()
{
	#define int long long
	freopen("str.in","r",stdin); freopen("str.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1,x,y,z;i<=m;i++)
	{
		x=read(); y=read(); z=read();
		s[i]=(Node){x,y,z};
		for(int j=1;j<=z;j++)
			if(find(x+j-1)!=find(y+j-1))
				if(find(y+j-1)>find(x+j-1)) fa[find(y+j-1)]=find(x+j-1);
				else fa[find(x+j-1)]=find(y+j-1);
	}
	for(int i=1;i<=m;i++)
	{
		int p1=s[i].x+s[i].z,p2=s[i].y+s[i].z;
		if(p1>n||p2>n) continue;
		if(find(p1)==find(p2)) printf("-1"),exit(0);
		if(find(p1)>find(p2)) swap(p1,p2);
		v[find(p2)].push_back(find(p1));
	}
	for(int i=1,col;i<=n;i++)
	{
		if(find(i)!=i) continue; bit.reset();
		for(auto it:v[i]) bit[ans[it]]=true;
		col=0; while(bit[col]) col++; ans[i]=col;
	}
	for(int i=1;i<=n;i++) printf("%lld ",ans[find(i)]);
	return 0;
}

T2 寻宝

解题思路

签到题。

首先把所有的可以互相到达的点用并茶几缩一下。

对于传送门的情况可以 Floyd 跑一遍也可以 bitset 整一遍,还可以对于一个询问直接搜一下。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=5e4+10;
int n,m,t,q,col,vis[N],fa[N];
int tot=1,head[N],ver[N<<1],nxt[N<<1];
int d1[10]={0,1,-1,0,0};
int d2[10]={0,0,0,1,-1};
vector<int> v[N];
char ch[N];
int id(int x,int y){return (x-1)*m+y;}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void add_edge(int x,int y)
{
	ver[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
void dfs(int x)
{
	vis[x]=col;
	for(int i=head[x];i;i=nxt[i])
		if(vis[ver[i]]^col)
			dfs(ver[i]);
}
#undef int
int main()
{
	#define int long long
	freopen("treasure.in","r",stdin); freopen("treasure.out","w",stdout);
	n=read(); m=read(); t=read(); q=read();
	for(int i=0;i<=m+1;i++) v[0].push_back('#'),v[n+1].push_back('#');
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ch+1); v[i].push_back('#');
		for(int j=1;j<=m;j++) v[i].push_back(ch[j]);
		v[i].push_back('#');
	}
	for(int i=1;i<=n*m;i++) fa[i]=i;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(v[i][j]!='#')
				for(int k=1;k<=4;k++)
				{
					int x=i+d1[k],y=j+d2[k];
					if(v[x][y]=='#') continue;
					int p1=id(i,j),p2=id(x,y);
					if(find(p1)!=find(p2)) fa[find(p1)]=find(p2);
				}
	for(int i=1,x,y,x2,y2;i<=t;i++)
	{
		x=read(); y=read(); x2=read(); y2=read();
		if(find(id(x,y))==find(id(x2,y2))) continue;
		add_edge(find(id(x,y)),find(id(x2,y2))); 
	}
	while(q--)
	{
		int x,y,x2,y2,p1,p2;
		x=read(); y=read(); x2=read(); y2=read();
		p1=find(id(x,y)); p2=find(id(x2,y2)); col++; dfs(p1);
		if(vis[p2]==col) printf("1\n"); else printf("0\n");
	}
	return 0;
}

T3 序列

解题思路

确实是一个李超线段树的好题。。。

发现一个位置 p 的最后答案其实就是对于以 p 为右端点向左边拓展,以 p+1 为左端点可以向右边拓展的最优解。

这两种情况类似只讨论第一种情况,记前缀和数组是 \(prea,preb\) 。

那么最优解就是 \(\max\limits_{1\le l\le r}\{(prea_r-prea_{l-1})-k(preb_r-preb_{l-1})\}\) 。

对于一个右端点 p 而言答案就是 \(prea_p-k\times preb_p+\max\limits_{0\le i<p}\{preb_i\times k-prea_i\}\) 。

直接以 k 为下标,李超线段树维护就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define ls x<<1
#define rs x<<1|1
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=1e6+10,INF=1e18;
int n,m,cnt,lim1,lim2=INF,a[N],b[N],lsh[N],prea[N],preb[N],sufa[N],sufb[N],ans[N];
struct node{int p,k;}q[N];
vector< pair<int,int> > pre[N],suf[N];
inline int g(int pos,int k,int b){return lsh[pos]*k+b;}
struct Segment_Tree
{
	struct Node{int k,b;}tre[N<<2];
	void build(int x,int l,int r)
	{
		tre[x].b=-INF; tre[x].k=0; if(l==r) return ;
		int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r);
	}
	void insert(int x,int l,int r,int k,int b)
	{
		if(g(l,k,b)>=g(l,tre[x].k,tre[x].b)&&g(r,k,b)>=g(r,tre[x].k,tre[x].b)) return tre[x]=(Node){k,b},void();
		if(l==r) return ; int mid=(l+r)>>1;
		if(g(mid,k,b)>g(mid,tre[x].k,tre[x].b)) swap(tre[x].k,k),swap(tre[x].b,b);
		if(g(l,k,b)>g(l,tre[x].k,tre[x].b)) insert(ls,l,mid,k,b);
		if(g(r,k,b)>g(r,tre[x].k,tre[x].b)) insert(rs,mid+1,r,k,b);
	}
	int query(int x,int l,int r,int pos)
	{
		if(l==r) return g(pos,tre[x].k,tre[x].b);
		int mid=(l+r)>>1,temp=g(pos,tre[x].k,tre[x].b);
		if(pos<=mid) return max(temp,query(ls,l,mid,pos));
		return max(temp,query(rs,mid+1,r,pos));
	}
}T1,T2;
#undef int
int main()
{
	#define int long long
	freopen("seq.in","r",stdin); freopen("seq.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=n;i++) a[i]=read(),b[i]=read(),prea[i]=prea[i-1]+a[i],preb[i]=preb[i-1]+b[i];
	for(int i=n;i>=1;i--) sufa[i]=sufa[i+1]+a[i],sufb[i]=sufb[i+1]+b[i];
	for(int i=1;i<=m;i++) q[i].p=read(),q[i].k=read(),lsh[i]=q[i].k,lim1=max(lim1,q[i].p),lim2=min(lim2,q[i].p+1);
	sort(lsh+1,lsh+m+1); cnt=unique(lsh+1,lsh+m+1)-lsh-1; T1.build(1,1,cnt); T2.build(1,1,cnt);
	for(int i=1;i<=m;i++)
	{
		pre[q[i].p].push_back({i,q[i].k});
		if(q[i].p!=n) suf[q[i].p+1].push_back({i,q[i].k});
	}
	for(int i=0;i<=lim1;i++)
	{
		for(auto it:pre[i])
		{
			int temp=lower_bound(lsh+1,lsh+cnt+1,it.second)-lsh;
			ans[it.first]+=prea[i]-it.second*preb[i]+T1.query(1,1,cnt,temp);
		}
		T1.insert(1,1,cnt,preb[i],-prea[i]);
	}
	for(int i=n+1;i>=lim2;i--)
	{
		T2.insert(1,1,cnt,sufb[i],-sufa[i]);
		for(auto it:suf[i])
		{
			int temp=lower_bound(lsh+1,lsh+cnt+1,it.second)-lsh;
			ans[it.first]+=sufa[i]-it.second*sufb[i]+T2.query(1,1,cnt,temp);
		}
	}
	for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
	return 0;
}

T4 构树

解题思路

只学会了一个状压的做法。。。

首先有一个定理:

Cayley定理:一个完全图有\(n^{n-2}\)棵无根生成树,经典问题prufer序列证明

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

然后我们枚举那些边是连接的然后根据上面的定理求出来一个至少有若干条边相同的值,然后二项式反演就好了。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"RP++"<<endl
#define count __builtin_popcount
using namespace std;
inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){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 N=8e3+10,mod=1e9+7;
int n,fac[N],ifac[N],g[N],f[N],fa[N],siz[N];
pair<int,int> s[N];
int power(int x,int y,int p=mod)
{
	int temp=1; y=(y+mod-1)%(mod-1);
	for(;y;y>>=1,x=x*x%p)
		if(y&1) temp=temp*x%p;
	return temp;
}
int find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]);
}
void add(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int C(int x,int y){if(x<y)return 0;return fac[x]*ifac[y]%mod*ifac[x-y]%mod;}
#undef int
int main()
{
	#define int long long
	freopen("tree.in","r",stdin); freopen("tree.out","w",stdout);
	n=read(); fac[0]=ifac[0]=1;
	for(int i=1,x,y;i<n;i++) s[i].first=read(),s[i].second=read();
	for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; ifac[n]=power(fac[n],mod-2);
	for(int i=n-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
	for(int sta=0;sta<(1ll<<n-1);sta++)
	{
		int temp=1;
		for(int i=1;i<=n;i++) fa[i]=i,siz[i]=1;
		for(int i=1;i<n;i++)
			if(((sta>>i-1)&1)&&find(s[i].first)!=find(s[i].second))
			{
				siz[find(s[i].second)]+=siz[find(s[i].first)];
				fa[find(s[i].first)]=find(s[i].second);
			}
		for(int i=1;i<=n;i++) if(find(i)==i) temp=temp*siz[i]%mod;
		add(g[count(sta)],temp);
	}
	for(int i=0;i<n;i++) g[i]=g[i]*power(n,n-i-2)%mod;
	for(int i=0;i<n;i++)
	{
		for(int j=i,bas=1;j<n;j++,bas=-bas)
			add(f[i],(bas+mod)%mod*C(j,i)%mod*g[j]%mod);
	}
	for(int i=0;i<n;i++) printf("%lld ",f[i]);
	return 0;
}

标签:return,NOIP,int,30,tre,long,find,97,define
来源: https://www.cnblogs.com/Varuxn/p/15552878.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有