计蒜客2019.12提高组月赛

T1 排列

这道题目考察的主要是贪心的构造方法,唯一的难点就在于奇数的特殊处理

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
int a[maxn],qs[maxn],qb[maxn];
int ans[maxn],ps,pb,ca,cb;
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	int n;
	ps=1; pb=1; ca=1; cb=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1;i<=n/2;i++) qs[ca++]=i;
	for(int i=n/2+1;i<=n;i++) qb[cb++]=i;
	int flag=n%2;
	for(int i=1;i<=n;i++)
	{
		if(a[i]<=(n+1)/2)
		{
			if(flag && a[i]==n/2+1 && ps<ca)
			{
				ans[i]=qs[ps++];
				if(pb<cb)
					qs[ca++]=qb[pb++];
				continue;
			}
			else
				ans[i]=qb[pb++];
			flag=0;
		}
		else
			ans[i]=qs[ps++];
	} 
	for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	return 0;
}

T2 逆序对

这道题目考察的主要是对于期望的理解??

对于一个1-n的数列删除了i个数以后,可以离散后得到一个1-(n-i)的数列

删除了i次后的逆序对数量也就是1-(n-i)的数列的逆序对的数量

问题就转化为求1-n随机数列的逆序对数量 对于两个数 x,y它们二者构成逆序对的可能性为1/2,而总对数为\frac{n(n+1)}{2}

所以可以得到期望数量为\frac{n(n+1)}{2}*\frac{1}{2}=\frac{n(n+1)}{4}

直接求个4的逆元一乘就做出来了

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353;
ll qpow(ll a,ll b)
{
	ll res=1;
	while(b)
	{
		if(b&1)
		{
			res=(res*a)%mod;
		}
		a=a*a%mod;
		b>>=1;
	}
	return res;
}
ll inv4=qpow(4,mod-2);
ll calc(int x)
{
	return ((1LL*x*(x-1)%mod)*inv4%mod);
}
int main()
{
	int n;
	scanf("%d",&n);
	for(int i=n;i>=2;i--)
		printf("%lld\n",calc(i));
	printf("%d\n%d\n",0,0);
	return 0;
}

T3 幸运路径

这道题目其实是树上差分很常见的一个思路——拆边

想要快速的计算一些边的和,我们把题目中的幸运边在两个点上+v,在两点之间的路径-v即可

然后就运用树上差分实现求点权和边权的和

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e5+5;
typedef long long ll;
int n,m,q;
struct eedge
{
	int to,nxt;
}e[maxn<<1];
int head[maxn],cnt;
void add(int x,int y)
{
	e[++cnt].to=y;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}
int lowbit(int x)
{
	return x&(-x);
}
struct qzh
{
	ll sum[maxn];
	void add(int x,ll y)
	{
		while(x<=n)
		{
			sum[x]+=y;
			x+=lowbit(x);
		}
	}
	ll query(ll x)
	{
		ll res=0;
		while(x>0)
		{
			res+=sum[x];
			x-=lowbit(x);
		}
		return res;
	}
}point,edge;
int f[maxn][20],dep[maxn],dfs_time,in[maxn],out[maxn];
void dfs(int u,int fa)
{
	dep[u]=dep[fa]+1;
	f[u][0]=fa;
	in[u]=++dfs_time;
	for(int i=head[u];i;i=e[i].nxt)
	{
		int to=e[i].to;
		if(to==fa) continue;
		dfs(to,u);
	}
	out[u]=dfs_time;
}
void init_lca()
{
	for(int j=1;j<=18;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];
}
int LCA(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=18;i>=0;i--)
		if(dep[f[x][i]]>=dep[y])
			x=f[x][i];
	if(x==y) return x;
	for(int i=18;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return f[x][0];
}
ll sump[maxn],sume[maxn];
void calc(int u)
{
	for(int i=head[u];i;i=e[i].nxt)
	{
		int to=e[i].to;
		if(to==f[u][0]) continue;
		sume[to]+=sume[u]; sump[to]+=sump[u]; 
		calc(to);
	}
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);
	scanf("%d%d%d",&n,&m,&q);
	int x,y,z;
	for(int i=1;i<n;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y); add(y,x);
	}
	dfs(1,0); init_lca();
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		int lca=LCA(x,y);
		point.add(in[x],-z);
		point.add(in[y],-z);
		point.add(in[lca],2*z);
		edge.add(in[x],z);
		edge.add(in[y],z);
		edge.add(in[lca],-z);
		if(f[lca][0])
			edge.add(in[f[lca][0]],-z);
	}
	for(int i=1;i<=n;i++)
	{
		sume[i]=edge.query(out[i])-edge.query(in[i]-1);
		sump[i]=point.query(out[i])-point.query(in[i]-1);
	}
	calc(1);
	for(int i=1;i<=q;i++)
	{
		ll ans=0;
		scanf("%d%d",&x,&y);
		int lca=LCA(x,y);
		ans=sume[x]+sume[y]-sume[lca]-sume[f[lca][0]];
		ans+=sump[x]+sump[y]-2*sump[lca];
		printf("%lld\n",ans);
	}
	return 0;
}

T4 优秀数

这道题目首先由数据范围很明显是和数位dp相关,再加上前面字符串的匹配问题可以发现可以AC自动机

题源:P3311 [SDOI2014] 数数

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn=2005;
const int mod=1e9+7;
char s[maxn];
int n,tr[maxn][11],cnt=1,ans,mk[maxn],fail[maxn];
void insert(char *ss)
{
	int len=strlen(ss+1),now=1;
	for(int i=1;i<=len;i++)
	{
		int dig=ss[i]-'0';
		if(!tr[now][dig]) tr[now][dig]=++cnt;
		now=tr[now][dig];
	}
	mk[now]=1;
}
void build()
{
	queue <int> q;
	for(int i=0;i<=9;i++) tr[0][i]=1;
	q.push(1);
	while(!q.empty())
	{
		int u=q.front(); q.pop();
		mk[u]|=mk[fail[u]];
		for(int i=0;i<=9;i++)
			if(tr[u][i])
			{
				fail[tr[u][i]]=tr[fail[u]][i];
				q.push(tr[u][i]);
			}
			else tr[u][i]=tr[fail[u]][i];
	}
}
int f[2][maxn][2];
void dp()
{
	int m=strlen(s+1);
	for(int i=1;i<=s[1]-'0';i++)
		if(!mk[tr[1][i]])
			f[1][tr[1][i]][i==s[1]-'0']++,f[1][tr[1][i]][i==s[1]-'0']%=mod;
	for(int i=2;i<=m;i++)
	{
		memset(f[i&1],0,sizeof(f[i&1]));
		for(int j=1;j<=9;j++)
			if(!mk[tr[1][j]])
				f[i&1][tr[1][j]][0]++,f[i&1][tr[1][j]][0]%=mod;
		for(int j=1;j<=cnt;j++)
		{
			if(mk[j]) continue;
			if(f[(i-1)&1][j][0])
				for(int dig=0;dig<=9;dig++)
					if(!mk[tr[j][dig]])
						(f[i&1][tr[j][dig]][0]+=f[(i-1)&1][j][0])%=mod;
			if(f[(i-1)&1][j][1])
				for(int dig=0;dig<=s[i]-'0';dig++)
					if(!mk[tr[j][dig]])
						(f[i&1][tr[j][dig]][dig==s[i]-'0']+=f[(i-1)&1][j][1])%=mod;
		}
	}
	for(int i=1;i<=cnt;i++)
		if(!mk[i])
			(((ans+=f[m&1][i][0])%=mod)+=f[m&1][i][1])%=mod;
}
int main()
{
	freopen("a.in","r",stdin);
	freopen("a.out","w",stdout);	
	scanf("%s\n%d",s+1,&n);
	char ss[maxn];
	for(int i=1;i<=n;i++)
	{
		scanf("%s",ss+1);
		insert(ss);
	}
	build();
	dp();
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/andyc_03/article/details/112550113
今日推荐