hgoi#20191101

T1-awesome

给定一个可重集合和一个质数 $ P $ ,求本质不同的乘积模 $ P $ 为 $ 1 $ 的三元组的数目。

解法

暴力分三种情况讨论
三个不同的数,两个不同的数,一个数
三个不同的数,先两两求出模 $ P $ 意义下的乘积
然后对于每个数询问其逆元
两个不同的数,一个数,这两种情况直接暴力枚举即可

ac代码

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
using namespace std; 
unordered_map<int,int>mp;
int n,P,cnt,x,ans,a[2500],s[2500],ni[2500],g[2500];
int p(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1ll*ret*a%P;
		a=1ll*a*a%P,b/=2;
	}
	return ret;
}
int main()
{
	freopen("awesome.in","r",stdin);
	freopen("awesome.out","w",stdout);
	scanf("%d%d",&n,&P);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if(!mp[x])
			a[++cnt]=x%P,ni[cnt]=p(x%P,P-2),mp[x]=cnt;
		s[mp[x]]++;
	}
	mp.clear();
	// 1 1 1
	for(int i=1;i<=cnt;i++)
		for(int j=1;j<=cnt;j++)
			if(i!=j)
			{
				int mo=1ll*a[i]*a[j]%P;
				mp[mo]++;
				if(mo==ni[i])g[i]++;
				if(mo==ni[j])g[j]++;
			}
	for(int i=1;i<=cnt;i++)
		if(mp.count(ni[i]))ans+=mp[ni[i]]-g[i];
	ans/=6;
	// 1 2
	for(int i=1;i<=cnt;i++)
		for(int j=1;j<=cnt;j++)
			if(i!=j&&s[j]>1)
				if(1ll*a[i]*a[j]%P*a[j]%P==1)
					ans++;
	// 3
	for(int i=1;i<=cnt;i++)
		if(s[i]>2)
			if(1ll*a[i]*a[i]%P*a[i]%P==1)
				ans++;
	printf("%d\n",ans);
	return 0;
}

T2-bag

二维平面上给定 $ n $ 个点,并给额外 $ m $ 个背包,每个背包有一个容量。
求一个二维偏序下最长链,满足可以给每个链上的点分配一个背包,使得每个点的 $ x $ 值都不超过其对应的背包容量。

解法

对于容量,可以直接排序使其递减
那么设 $ f_i $ 表示链的长度为 $ i $ 时最小的 $ y $ 值
就是一个带限制的最长不上升子序列
$ x $ 值大的点一定优先塞到容量大的背包内
之后就和最长不上升子序列一样做即可

ac代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define mid (l+r>>1)
using namespace std;
struct node
{
	int w,v;
	void init(){scanf("%d%d",&w,&v);}
	bool operator<(const node&a)const{return w==a.w?v>a.v:w>a.w;}
}a[100010];
int T,n,m,cnt,ans,l,r,ret,t[100010],p[200010],f[100010];
int cmp(int a,int b){return a>b;}
int id(int x){return lower_bound(p+1,p+cnt+1,x)-p;}
int main()
{
	scanf("%d",&T);
	while(T--)
	{
		memset(f,0,sizeof(f)),f[0]=inf,ans=cnt=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)a[i].init(),p[++cnt]=a[i].w;
		scanf("%d",&m);
		for(int i=1;i<=m;i++)scanf("%d",&t[i]),p[++cnt]=t[i];
		sort(a+1,a+n+1),sort(t+1,t+m+1,cmp),sort(p+1,p+cnt+1);
		cnt=unique(p+1,p+cnt+1)-p-1;
		for(int i=1;i<=n;i++)a[i].w=id(a[i].w);
		for(int i=1;i<=m;i++)t[i]=id(t[i]);
		for(int i=1;i<=n;i++)
		{
			l=1,r=m,ret=-1;
			while(l<=r)
				if(t[mid]>=a[i].w)ret=mid,l=mid+1;
				else r=mid-1;
			if(ret==-1)continue;
			l=0,r=ret-1,ret=-1;
			while(l<=r)
				if(f[mid]>=a[i].v)ret=mid,l=mid+1;
				else r=mid-1;
			ans=max(ans,ret+1),f[ret+1]=max(f[ret+1],a[i].v);
		}
		printf("%d\n",ans);
	}
	return 0;
}

T3-subtree

合法的有根树满足下列条件:每个点的父亲编号比自己小且所有子树大小不与任一规定的禁止值相等
求 $ n $ 个节点深度为 $ j $ 的合法有根树数目模 $ 998244353 $ ,其中 $ L \leq j \leq R $ 。

解法

设 $ dp_{i,d} $ 表示 $ i $ 个节点深度不超过 $ d $ 的方案数
如果没有子树大小的限制,转移如下:
$ dp_{i,d}= \sum\limits_{j=1}^{i-1} dp_{i-j,d}×dp_{j,d-1}×C_{i-2}^{j-1} $
对于限制,只要转移的时候把 $ dp_{j,d-1} $ 看成 $ 0 $ 即可

ac代码

#include<bits/stdc++.h>
#define mod 998244353 
using namespace std;
int n,k,l,r,x,a[510],C[510][510],dp[510][510];
int main()
{
	scanf("%d%d",&n,&k);
	for(int i=0;i<=n;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
	}
	for(int i=1;i<=k;i++)
		scanf("%d",&x),a[x]=1;
	dp[1][1]=!a[1];
	for(int d=2;d<=n;d++)
	{
		dp[1][d]=1;
		for(int i=2;i<=n;i++)
		{
			for(int j=1;j<i;j++)
				dp[i][d]=(dp[i][d]+1ll*dp[i-j][d]*dp[j][d-1]%mod*C[i-2][j-1])%mod;
		}
		for(int i=1;i<=n;i++)
			if(a[i])dp[i][d]=0;
	}
	scanf("%d%d",&l,&r);
	for(int i=l;i<=r;i++)
		printf("%d ",(dp[n][i]-dp[n][i-1]+mod)%mod);
    return 0;
}
posted @ 2019-11-01 13:36  可耐滴小慕容  阅读(129)  评论(0编辑  收藏  举报