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;
}

浙公网安备 33010602011771号