冲刺 NOIP2021 模拟赛 B 组 Day4

写在前面

唔……话说我再来半个小时可能T4就A了……恰好我又少了半小时打……
结论:以后作业最好慢点写
《论同时开3场比赛还居然有一场AK这档事》(虽然偏偏是最水的一场)
结论:以后打比赛尽量多开

T1

唔话说T1是啥来着?
简而言之就是这样,那样,然后就A了
定义一个集合 S S S f f f函数为 f ( S ) = m a x ( a ) − m i n ( a ) ( a ∈ S ) f(S)=max(a)-min(a)(a∈S) f(S)=max(a)min(a)(aS)
给定一个集合 S S S,求该集合所有非空子集的f函数之和。对1e9+7取模。

思路

2 n ∗ 2^n* 2n最大值+ 2 n − 1 ∗ 2^{n-1}* 2n1次大值+……+最小值- 2 n ∗ 2^n* 2n最小值- 2 n − 1 ∗ 2^{n-1}* 2n1次小值-……-最大值
code:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline long long read()
{
	long long ret,c,f=1;
	while (((c=getchar())> '9'||c< '0')&&c!='-');
	if (c=='-') f=-1,ret=0;
	else ret=c-'0';
	while ((c=getchar())>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+(c^48);
	return ret*f;
}
long long a[1000006],n,c=1,u;
long long ans;
long long ksm(long long x,long long y)
{
	long long ans=1;
	while (y)
	{
		if (y&1) ans=ans*x%1000000007;
		x=x*x%1000000007;
		y>>=1;
	}
	return ans;
}
int main()
{
	freopen("sum.in","r",stdin);
	freopen("sum.out","w",stdout);
	n=read();
	for (int i=1;i<=n;i++) a[i]=read();
	sort(a+1,a+1+n);
	c=ksm(2,n);
	for (int i=1;i<=n;i++)
	{
		c=c*500000004%1000000007;
		u=c%1000000007*(1000000007-a[i])%1000000007;
		ans=(ans+u)%1000000007;
	}
	c=ksm(2,n);
	for (int i=n;i>=1;i--)
	{
		c=c*500000004%1000000007;
		u=c%1000000007*a[i]%1000000007;
		ans=(ans+u)%1000000007;
	}
	cout<<ans;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T2

有一个长为 n ∗ k n*k nk的数组,它是由长为 n n n的数组 A A A重复 k k k次得到的。
定义这个数组的一个区间的权值为它里面不同的数的个数,现在,你需要求出对于这个数组的每个非空区间的权值之和。
答案对1e9+7取模。
怎么什么题都喜欢对1e9+7取mod啊

思路

数组可以认为是 A A … … A AA……A AAA
考虑每个点贡献的区间个数,等于前面相同的位置到该点的距离 ∗ * 该点到最后的距离
那么从第2组A开始,前面一项已经固定,把后面的用等差数列求和就好
code:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
inline long long read()
{
	long long ret,c,f=1;
	while (((c=getchar())> '9'||c< '0')&&c!='-');
	if (c=='-') f=-1,ret=0;
	else ret=c-'0';
	while ((c=getchar())>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+(c^48);
	return ret*f;
}
map<long long,long long> b;
long long a[400006],pre[400006],n,k,tot;
long long ans,ans2;
int main()
{
	freopen("loop.in","r",stdin);
	freopen("loop.out","w",stdout);
	n=read(),k=read();
	for (int i=1;i<=n;i++)
	{
		a[i]=read();
		if (b.find(a[i])==b.end())
		{
			b[a[i]]=++tot;
		}
		a[i]=b[a[i]];
	}
	for (int i=1;i<=n;i++) a[i+n]=a[i];
	b.clear();
	for (int i=1;i<=2*n;i++)
	{
		pre[i]=b[a[i]];
		b[a[i]]=i;
		if (i>n) ans=(ans+(i-pre[i])*(((k*n-i+1)%1000000007+(n-i+n+1)%1000000007)%1000000007)%1000000007*(k-1)%1000000007*500000004%1000000007)%1000000007;
		else ans2=(ans2+(i-pre[i])*(k*n-i+1)%1000000007)%1000000007;
	}
	if (k==1) ans=0;
	cout<<(ans+ans2)%1000000007;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T3

有一棵 n n n个点的树和两个整数 ,求满足以下条件的四元组 ( a , b , c , d ) (a,b,c,d) (a,b,c,d)的个数:

  1. 1 < = a , b , c , d < = n 1<=a,b,c,d<=n 1<=a,b,c,d<=n
  2. a − > b a->b a>b的长度为 p p p c − > d c->d c>d的长度为 q q q
  3. 路径 ( a , b ) (a,b) (a,b) ( c , d ) (c,d) (c,d)之间没有交集(没有公共点)。

思路

考虑dp。
这里不给方程,因为那玩意不如看代码
s o n i , j son_{i,j} soni,j表示子树中到i的距离为j的点个数
f a t i , j fat_{i,j} fati,j表示除去i的子树到i的距离为j的点个数
那么 f p i , f q i fp_i,fq_i fpi,fqi分别表示i的子树中过i连向另一个儿子子树的距离为p/q的方案数,
g p i , g q i gp_i,gq_i gpi,gqi分别表示i的子树中过i连向子树外的距离为p/q的方案数,
那么考虑容斥,答案变成所有-相交。
code:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;
inline long long read()
{
	long long ret,c,f=1;
	while (((c=getchar())> '9'||c< '0')&&c!='-');
	if (c=='-') f=-1,ret=0;
	else ret=c-'0';
	while ((c=getchar())>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+(c^48);
	return ret*f;
}
long long n,p,q,head[3003],to[6006],son[3003][3003],nxt[6006],fa[3003],fp[3003],gp[3003],gq[3003],fq[3003],fat[3003][3003],tot;
void add(long long x,long long y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
	return;
}
void dfs1(long long x,long long f)
{
	fa[x]=f;
	son[x][0]=1;
	for (long long i=head[x];i;i=nxt[i])
	{
		if (fa[x]!=to[i])
		{
			dfs1(to[i],x);
			for (long long j=1;j<=p;j++) fp[x]+=son[x][p-j]*son[to[i]][j-1];
			for (long long j=1;j<=q;j++) fq[x]+=son[x][q-j]*son[to[i]][j-1];
			for (long long j=1;j<=n;j++) son[x][j]+=son[to[i]][j-1];
		}
	}
	return;
}
void dfs2(long long x)
{
	for (long long j=1;j<=p;j++) gp[x]+=son[x][p-j]*fat[x][j];
	for (long long j=1;j<=q;j++) gq[x]+=son[x][q-j]*fat[x][j];
	for (long long i=head[x];i;i=nxt[i])
	{
		if (fa[x]!=to[i])
		{
			fat[to[i]][1]=1;
			for (long long j=2;j<=n;j++) fat[to[i]][j]+=fat[x][j-1]+son[x][j-1]-son[to[i]][j-2];
			dfs2(to[i]);
		}
	}
	return;
}
int main()
{
	freopen("intersection.in","r",stdin);
	freopen("intersection.out","w",stdout);
	n=read(),p=read(),q=read();
	for (long long i=1;i<n;i++)
	{
		long long x=read(),y=read();
		add(x,y),add(y,x);
	}
	dfs1(1,0);
	dfs2(1);
	long long ans1=0,ans2=0;
	for (int i=1;i<=n;i++) ans1+=fp[i],ans2+=fq[i];
	ans1*=ans2;
	for (int i=1;i<=n;i++) ans1-=fp[i]*fq[i]+fq[i]*gp[i]+fp[i]*gq[i];
	cout<<(ans1<<2);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

T4

给无向连通图一张,其中k条边必须走,求从1开始到1结束的最短路径。(k<=12)
但是除了k,其他的范围都假了(缩水1/3起步)

思路

k的范围启发我们使用状压dp。
b i , j b_{i,j} bi,j表示状态i下,结尾为j的最短路。
然后可以dij预处理这25个点到所有位置的最短路。
然后dp(见代码)
code:

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
inline long long read()
{
	long long ret,c,f=1;
	while (((c=getchar())> '9'||c< '0')&&c!='-');
	if (c=='-') f=-1,ret=0;
	else ret=c-'0';
	while ((c=getchar())>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+(c^48);
	return ret*f;
}
long long b[(1<<13)+10][45],dis[45][1000005],head[1000005],to[1000006],w[1000006],nxt[1000006],tot,n,m,k;
void add(long long x,long long y,long long ww)
{
	to[++tot]=y;
	w[tot]=ww;
	nxt[tot]=head[x];
	head[x]=tot;
	return;
}
struct f{
	long long x,y;
} p;
bool operator <(const f &a,const f &b)
{
	return a.y>b.y;
}
bool book[1000005];
priority_queue<f> o;
int main()
{
	freopen("bridge.in","r",stdin);
	freopen("bridge.out","w",stdout);
	b[0][1]=0;
	n=read(),m=read(),k=read();
	for (long long i=1;i<(1<<k);i++) for (long long j=1;j<=2*k+1;j++) b[i][j]=1000000000000000001ll;
	for (long long i=1;i<=n;i++) for (long long j=1;j<=2*k+1;j++) dis[j][i]=1000000000000000001ll;
	dis[1][0]=1;
	for (long long i=1;i<=k;i++)
	{
		dis[2*i][0]=read(),dis[2*i+1][0]=read();
		long long z=read();
		add(dis[2*i][0],dis[2*i+1][0],z),add(dis[2*i+1][0],dis[2*i][0],z);
	}
	for (long long i=k+1;i<=m;i++)
	{
		long long x=read(),y=read(),z=read();
		add(x,y,z),add(y,x,z);
	}
	for (long long i=1;i<=2*k+1;i++)
	{
		p.x=dis[i][0],p.y=0;
		dis[i][p.x]=0;
		o.push(p);
		memset(book,0,sizeof(book));
		while (o.size())
		{
			long long x=o.top().x;
			o.pop();
			if (book[x]) continue;
			book[x]=1;
			for (long long j=head[x];j;j=nxt[j])
			{
				if (dis[i][to[j]]>dis[i][x]+w[j])
				{
					dis[i][to[j]]=dis[i][x]+w[j];
					p.x=to[j],p.y=dis[i][to[j]];
					o.push(p);
				}
			}
		}
	}
	for (long long i=1;i<(1<<k);i++)
	{
		for (long long j=1;j<=k;j++)
		{
			if ((1<<(j-1))&i)
			{
				if ((1<<(j-1))==i)
				{
					b[i][2*j+1]=min(b[i][2*j+1],b[i^(1<<(j-1))][1]+dis[1][dis[2*j][0]]+w[2*j]);
					b[i][2*j+1]=min(b[i][2*j+1],b[i^(1<<(j-1))][1]+dis[1][dis[2*j][0]]+w[2*j]);
					b[i][2*j]=min(b[i][2*j],b[i^(1<<(j-1))][1]+dis[1][dis[2*j+1][0]]+w[2*j]);
					b[i][2*j]=min(b[i][2*j],b[i^(1<<(j-1))][1]+dis[1][dis[2*j+1][0]]+w[2*j]);
				}
				for (long long l=1;l<=k;l++)
				{
					if (j!=l&&(1<<(l-1))&i)
					{
						b[i][2*j+1]=min(b[i][2*j+1],b[i^(1<<(j-1))][2*l]+dis[2*l][dis[2*j][0]]+w[2*j]);
						b[i][2*j+1]=min(b[i][2*j+1],b[i^(1<<(j-1))][2*l+1]+dis[2*l+1][dis[2*j][0]]+w[2*j]);
						b[i][2*j]=min(b[i][2*j],b[i^(1<<(j-1))][2*l]+dis[2*l][dis[2*j+1][0]]+w[2*j]);
						b[i][2*j]=min(b[i][2*j],b[i^(1<<(j-1))][2*l+1]+dis[2*l+1][dis[2*j+1][0]]+w[2*j]);
					}
				}
			}
		}
	}
	long long ans=1000000000000000001ll;
	for (long long i=1;i<=2*k+1;i++)
	{
		ans=min(ans,b[(1<<k)-1][i]+dis[i][1]);
	}
	cout<<ans;
	fclose(stdin);
	fclose(stdout);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值