2022/8/22 总结

A.函数变换

  • 花了两个小时试图使用排列组合解决,然而最后发现居然是个结论题……

  • 我果然是和结论题有仇吧

Solution

  • 打个表,就能发现当 \(n\) 确定时,\(ans_m\) 的值有公式可推:
    • \(1\le i<m\) 时,\(ans_i=2^{i-1}\)
    • \(i\ge m\) 时,\(ans_i=\sum\limits_{j=i-m}^{i-1} ans_j\)
AC code
#include<bits/stdc++.h>
using namespace std;

const int N=1e6+10;
const int mod=1e9+9;

#define ll long long
#define re register

int n,m;
ll mul[N<<1];
ll ans=0;

int main(){
//	freopen("change.in","r",stdin);
//	freopen("change.out","w",stdout);
	scanf("%d%d",&m,&n);
	if(m==1){
		printf("%d",0);
		return 0;
	}
	mul[0]=0;
	mul[1]=1;
	for(int i=2;i<m;++i)
		mul[i]=(1ll*mul[i-1]*2)%mod;
	if(n<m){
		printf("%d",mul[n]);
		return 0;
	}
	for(int i=1;i<=m;++i)
		(mul[i]+=mul[i-1])%=mod;
	for(int i=m;i<=n;++i){
		(mul[i]=(mul[i-1]-mul[i-m-1])%mod+mod)%=mod;
		(mul[i]+=mul[i-1])%=mod;
	}
	printf("%lld",((mul[n-1]-mul[n-m-1])%mod+mod)%mod);
	return 0;
}

B.刺客信条

  • 这美妙的名字,这美妙的题目背景……;

Solution

  • 二分答案 + 并查集;

  • 二分答案显而易见。说一下并查集。
    对于每个待测的 \(r\),对每个红衣教徒建立并查集。如果这个教徒身周以 \(r\) 为半径的圆中最远点越过了房间边界,就按照左下和右上分为两个集合。
    其次再扫描每两个点,如果这两个点为圆心的圆有相交,则加入一个集合;
    最后扫描每个左下和右上的点,如果有在同一集合中的(即直接或间接相交的),则说明这个 \(r\) 大了;

另外,调代码的时候发现 \(a\times a\)\(\mathtt{pow(} a,2.0\mathtt{)}\) 要快很多。

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=2005;
const double eps=1e-3;

#define re register

int X,Y,n;
int dx[N],dy[N];
int f[N];
int point[2][N],cnt[2];

double g(int a,int b,int c,int d){
	return 1.0*(c-a)*(c-a)+1.0*(d-b)*(d-b);
}

int get(int x){
	if(x==f[x]) return x;
	return f[x]=get(f[x]);
}

void merge(int x,int y){
	f[get(x)]=get(y);
	return ;
}

bool check(double r){
	cnt[0]=cnt[1]=0;
	for(int i=1;i<=n;++i)
		f[i]=i;
	for(re int i=1;i<=n;++i){
		if(dx[i]-r<=0 || dy[i]+r>=Y)
			point[0][++cnt[0]]=i;
		if(dx[i]+r>=X || dy[i]-r<=0)
			point[1][++cnt[1]]=i;
	}
	for(re int i=1;i<=n;++i)
		for(re int j=i+1;j<=n;++j){
			if(f[i]==f[j]) continue;
			if(g(dx[i],dy[i],dx[j],dy[j])<=4.0*r*r)
				merge(i,j);
		}
	for(re int i=1;i<=cnt[0];++i)
		for(re int j=1;j<=cnt[1];++j)
			if(get(point[0][i])==get(point[1][j]))
				return 0;
	return 1;
}

int main(){
//	freopen("kill.in","r",stdin);
//	freopen("kill.out","w",stdout);
	X=read(),Y=read(),n=read();
	for(re int i=1;i<=n;++i)
		dx[i]=read(),dy[i]=read();
	re double l=0,r=1e6;
	while(r-l>eps){
//		cout<<l<<" "<<r<<endl;
		re double mid=(l+r)/2.0;
		if(check(mid))
			l=mid;
		else r=mid;
	}
	printf("%.2f",l);
	return 0;
}

C.买玩具

D.摘果子

  • 考场上题都没看……

Solution

  • 树上背包。

  • 先跑一遍 \(\mathtt{dfs}\),存一下这棵树的 \(\mathtt{dfs}\) 序构成的序列和每个节点及其子树的大小。然后跑 \(01\) 背包就行了。

AC code
#include<bits/stdc++.h>
using namespace std;

inline int read(){
	int s=0,f=1;
	char ch=getchar();
	while(!isdigit(ch)){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch)){
		s=s*10+int(ch-'0');
		ch=getchar();
	}
	return s*f;
}

const int N=2010;

int n,m;
int v[N],p[N];
int head[N],ver[N<<1],nxt[N<<1],tot=0;
int a[N],siz[N],cnt=0;
int f[N][N];

void add(int x,int y){
	ver[++tot]=y,nxt[tot]=head[x],head[x]=tot;
	return ;
}

void dfs(int x,int fa){
	a[++cnt]=x;
	siz[x]=1;
	for(int i=head[x];i;i=nxt[i]){
		if(ver[i]==fa) continue;
		dfs(ver[i],x);
		siz[x]+=siz[ver[i]];
	}
	return ;
}

int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i)
		v[i]=read(),p[i]=read();
	int x,y;
	for(int i=1;i<n;++i){
		x=read(),y=read();
		add(x,y);
		add(y,x);
	}
	dfs(1,0);
	memset(f,-0x3f,sizeof(f));
	f[1][0]=0;
	for(int i=1;i<=n;++i){
		for(int j=0;j<=m;++j){
			if(j+p[a[i]]<=m){
				f[i+1][j+p[a[i]]]=max(f[i+1][j+p[a[i]]],f[i][j]+v[a[i]]);
				f[i+siz[a[i]]][j+p[a[i]]]=max(f[i+siz[a[i]]][j+p[a[i]]],f[i][j]+v[a[i]]);
			}
			f[i+siz[a[i]]][j]=max(f[i+siz[a[i]]][j],f[i][j]);
		}
	}
	int ans=0;
	for(int i=0;i<=m;++i)
		ans=max(ans,f[n+1][i]);
	printf("%d",ans);
	return 0;
}
posted @ 2022-08-22 18:29  Star_LIcsAy  阅读(57)  评论(0)    收藏  举报