天天看點

2019/11/02 校内模拟

T1 極好的問題

不是很難的題,考場上抽了,有一個細節挂了隻有 80 80 80 分。

由于要求本質不同的三元組 ( x , y , z ) (x,y,z) (x,y,z) 數量,我分了三種情況:

  • ( a , a , a ) (a,a,a) (a,a,a)。直接判一下即可。
  • ( a , a , b ) (a,a,b) (a,a,b)。 O ( n 2 ) O(n^2) O(n2) 枚舉 a , b a,b a,b 判斷即可。
  • ( a , b , c ) (a,b,c) (a,b,c)。我的做法是枚舉 b , c b,c b,c,把 b b b 之前的 a a a 的逆元加進 map 裡,然後直接詢問就可以了。

考場上由于怕 map 跑的太慢,就手寫了哈希表。

#include<bits/stdc++.h>
using namespace std;
const int N=2505;
int n,P,a[N],b[N],num[N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
int Inv(int x)  {return power(x,P-2);}
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
namespace Hash_Table{
	const int N=1e6+10,mod=1e6+3;
	int t,first[N],v[N],w[N],nxt[N];
	void add(int x,int y){
		nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=1;
	}
	void ADD(int x,int val){
		int pos=x%mod;
		for(int i=first[pos];i;i=nxt[i])
			if(v[i]==x)  {w[i]+=val;return;}
		add(pos,x);
	}
	int Query(int x){
		int pos=x%mod;
		for(int i=first[pos];i;i=nxt[i])
			if(v[i]==x)  return w[i];
		return 0;
	}
}
using namespace Hash_Table;
int m,tot;
int main(){
	scanf("%d%d",&n,&P);
	for(int i=1,x;i<=n;++i){
		scanf("%d",&x);
		if(x%P)  a[++tot]=x;
	}
	sort(a+1,a+tot+1);
	for(int i=1;i<=tot;++i){
		a[i]==a[i-1]?num[m]++:(b[++m]=a[i],num[m]=1);
	}
	int ans=0;
	for(int i=1;i<=m;++i){
		for(int j=i+1;j<=m;++j)
			ans+=Query(mul(b[i],b[j]));
		ADD(Inv(b[i]%P),1);
	}
	for(int i=1;i<=m;++i){
		if(num[i]>=3)  ans+=(power(b[i],3)==1);
		for(int j=1;j<=m;++j)
			if(i!=j&&num[i]>=2)  ans+=(mul(mul(b[i],b[i]),b[j])==1);
	}
	printf("%d\n",ans);
	return 0;
}
           

T2 背包問題

按照 w w w 排序之後,這就有點像 LIS 問題了,但是有背包容量的限制。

一個顯然的貪心是,如果我們選了 k k k 個物品,那麼用容量最大的 k k k 個背包會最優。

那麼按照背包容量從大到小排序,物品按照 w w w 從大到小排序。由于還有 v v v 的限制,我們需要樹狀數組來存答案。每次更新答案的時候在樹狀數組裡查 ≥ v \geq v ≥v 部分的最大答案,判斷背包的限制然後更新答案即可。

時間複雜度 O ( n log ⁡ n ) O(n\log n) O(nlogn)。

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m,k,bag[N],d[N];
struct node{int w,v;}p[N];
bool operator<(const node &p,const node &q)  {return (p.w==q.w)?p.v>q.v:p.w>q.w;}
void discrete(){
	sort(d+1,d+n+1),k=unique(d+1,d+n+1)-d-1;
	for(int i=1;i<=n;++i)  p[i].v=lower_bound(d+1,d+k+1,p[i].v)-d;
}
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
namespace BIT{
	int bit[N];
	#define lowbit(x) (x&(-x))
	void Clear()  {memset(bit,0,sizeof(bit));}
	void add(int x,int val)  {for(;x;x-=lowbit(x))Max(bit[x],val);}
	int Query(int x,int ans=0)  {for(;x<=k;x+=lowbit(x))Max(ans,bit[x]);return ans;}
	#undef lowbit
}
int main(){
	int T=in();
	while(T--){
		n=in();
		for(int i=1;i<=n;++i){
			p[i].w=in(),p[i].v=in(),d[i]=p[i].v;
		}
		sort(p+1,p+n+1),discrete();
		m=in();
		for(int i=1;i<=m;++i)  bag[i]=in();
		sort(bag+1,bag+m+1,greater<int>());
		int pos=0,ans=0;
		BIT::Clear();
		for(int i=1;i<=n;++i){
			while(pos<m&&bag[pos+1]>=p[i].w)  ++pos;
			int now=min(BIT::Query(p[i].v)+1,pos);
			BIT::add(p[i].v,now),Max(ans,now);
		}
		printf("%d\n",ans);
	}
	return 0;
}
           

T3 子樹問題

計數類 d p dp dp 題。永遠都想不到。。

設 f [ i ] [ j ] f[i][j] f[i][j] 表示有 i i i 節點的深度不超過 d d d 的有根樹的數量。先不考慮限制,則:

f [ i ] [ j ] = ∑ k = 1 i − 1 f [ i − k ] [ j ] × f [ k ] [ j − 1 ] × ( i − 2 j − 1 ) f[i][j]=\sum_{k=1}^{i-1}f[i-k][j]\times f[k][j-1]\times \binom{i-2}{j-1} f[i][j]=k=1∑i−1​f[i−k][j]×f[k][j−1]×(j−1i−2​)

後面乘的組合數就是給這些點配置設定标号。

對于數量限制的點,直接把 f [ i ] [ j ] f[i][j] f[i][j] 置成 0 0 0 即可。

最後 d d d 的答案就是 f [ n ] [ d ] − f [ n ] [ d − 1 ] f[n][d]-f[n][d-1] f[n][d]−f[n][d−1]。

時間複雜度 O ( n 3 ) O(n^3) O(n3)。

#include<bits/stdc++.h>
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=505,P=998244353;
int n,k,ban[N],L,R,C[N][N],f[N][N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
void prework(){
	C[0][0]=1;
	for(int i=1;i<=n;++i){
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;++j)  C[i][j]=add(C[i-1][j],C[i-1][j-1]);
	}
}
int main(){
	n=in(),k=in(),prework();
	for(int i=1,x;i<=k;++i)  ban[in()]=1;
	L=in(),R=in();
	f[1][1]=ban[1]?0:1;
	for(int d=2;d<=n;++d){
		for(int i=1;i<=n;++i){
			if(i==1)  {f[i][d]=1;continue;}
			for(int j=1;j<i;++j)  f[i][d]=add(f[i][d],mul(mul(f[i-j][d],f[j][d-1]),C[i-2][j-1]));
		}
		for(int i=1;i<=n;++i)  if(ban[i])  f[i][d]=0;
	}
	for(int i=L;i<=R;++i)  printf("%d ",dec(f[n][i],f[n][i-1]));
	return 0;
}