自嗨测试赛4
简单的树叶
设根节点答案为 \(x\),假设节点上操作为 \(min\),则要求相邻子节点权值都大于等于 \(x\),假设操作为 \(max\),则相邻子节点权值有 \(1\) 个大于等于 \(x\) 即可,这样我们会发现,如果一个 \(x\) 足够大,那我们必须能够按照这样的操作,在一些叶子节点中放入足够的大于等于 \(x\) 的数。
因为我们要让根节点最大,所以可以看作由根节点出发,找出所有必须大于等于 \(x\) 的叶子,给它们分别填上最大的数,只要使这些叶子的总个数 \(cnt\) 最小即可,答案就是 \(k-cnt+1\)。我们要求根节点能到达的最小叶子数,发现限制数量的是 \(max\) 操作,所以我们对于一个是 \(max\) 操作的点,从其儿子中选出一个能到达叶子数最小的即可,而 \(min\) 操作的点可以到达所有儿子的叶子。
用 \(f[i]\) 表示 \(i\) 最少能到达的叶子个数
\(f[i]=\sum_{j\in son(i)}f[j]\), \(i\) 是 \(min\) 操作
\(f[i]=min_{j\in son(i)}f[j]\), \(i\) 是 \(max\) 操作
\(f[i]=1\), \(i\) 是叶子
最终答案即为 \(k-f[1]+1\),复杂度 \(O(n)\),期望得分 \(100\)
Code
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=3e5+10;
int n,cnt,k,a[maxn],head[maxn];
struct Edge{
int to,nxt;
}e[maxn*2];
void add(int x,int y){
e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}
int dfs(int x){
if(!head[x]) return ++k,1;
int mn=1e9,sum=0;
for(int i=head[x];i;i=e[i].nxt){
int tmp=dfs(e[i].to);
mn=min(mn,tmp);
sum+=tmp;
}
return a[x]?mn:sum;
}
int main(){
freopen("leaf.in","r",stdin);
freopen("leaf.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=2;i<=n;++i){
int x;scanf("%d",&x);
add(x,i);
}
int tmp=dfs(1);
printf("%d\n",k-tmp+1);
return 0;
}
老姚的服装
预处理f[i][j]表示i,j作为左上角能向右下拓展的最大正方形边长,然后对于每个点i,j,可以O(1)求出以该点作为左上角能产生的最大答案是多少,假设答案为ans,我们就进行g[ans][i][j]++,最后对于每个ans,做一个二维前缀和
对于询问,我们二分一个值len,表示四分之一正方形的边长,若(x1,y1)到 \((x2-len*2+1,y2-len*2+1)\) 中有能作为左上角的,且最大答案为len的点,那么l=mid+1,否则r=mid-1
判断是否存在,因为已经对g数组后两维做了二维前缀和,直接差分判即可
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int n,m,q,f[505][505],g[255][505][505];
char s[505][505];
int main(){
freopen("clothes.in","r",stdin);
freopen("clothes.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n;++i) scanf("%s",s[i]+1);
for(int i=n;i>=1;--i)
for(int j=m;j>=1;--j){
f[i][j]=1;
if(s[i][j]==s[i+1][j]&&s[i][j]==s[i][j+1]&&s[i][j]==s[i+1][j+1])
f[i][j]+=min(f[i+1][j],min(f[i][j+1],f[i+1][j+1]));
}
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j){
int len=f[i][j];
if(s[i][j]!='B' || i+len*2-1>n || j+len*2-1>m) continue;
if(f[i+len][j]!=len || s[i+len][j]!='P') continue;
if(f[i][j+len]!=len || s[i][j+len]!='W') continue;
if(f[i+len][j+len]<len || s[i+len][j+len]!='G') continue;
g[len][i][j]++;
}
for(int i=0;i<=250;++i)
for(int j=1;j<=n;++j)
for(int k=1;k<=m;++k)
g[i][j][k]+=g[i][j-1][k]+g[i][j][k-1]-g[i][j-1][k-1];
for(int i=1;i<=q;++i){
int x,y,xx,yy;scanf("%d%d%d%d",&x,&y,&xx,&yy);
if(x>xx) swap(x,xx);
if(y>yy) swap(y,yy);
int l=1,r=min((xx-x+1)/2,(yy-y+1)/2),mid;
while(l<=r){
mid=(l+r)/2;
int rx=xx-mid*2+1,ry=yy-mid*2+1;
if(g[mid][rx][ry]-g[mid][rx][y-1]-g[mid][x-1][ry]+g[mid][x-1][y-1]) l=mid+1;
else r=mid-1;
}
printf("%d\n",r*r*4);
}
return 0;
}
Shawk的游戏
最终答案一定可以表示成将一个主干放在第一层,其他的小分叉放在第二层。
我们就去枚举每个点作为第一层,如果周围不能放在第二层的点超过2个,那么不合法,直接输No
考虑什么样的点不能放在第二层,设枚举x在主干上,对于邻接点y
-
度数大于3的
-
度数为3的且除x以外任一儿子后面有分叉的
-
度数为2的且除x以外的儿子后面有分叉的
现在我们考虑如何判分叉,一种方法是从所有的叶子开始,将所有相邻的度数小于等于2的点都删去(标记),如果有没删的点,那一定是分叉点,
然后我们对于每个点开一个数组c,c[x]为其所有相邻的点的vis标记之和,意思就是周围有几个儿子及后面没有分叉(也就是周围连了几条链)
现在我们可以判分叉了,然后对于每个点求出周围不能放第二层的点的个数,如果个数大于2,肯定不合法,否则合法
Code
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
int n,cnt,c[maxn],d[maxn],head[maxn];
bool vis[maxn];
struct Edge{
int to,nxt;
}e[maxn*2];
void add(int x,int y){
e[++cnt]=(Edge){y,head[x]},head[x]=cnt;
}
void del_chain(int x){
vis[x]=1;
for(int i=head[x];i;i=e[i].nxt)
if(!vis[e[i].to]&&d[e[i].to]<=2) del_chain(e[i].to);
}
int main(){
freopen("sgame.in","r",stdin);
freopen("sgame.out","w",stdout);
while(scanf("%d",&n)!=EOF){
memset(head,0,sizeof(head));
memset(vis,0,sizeof(vis));
memset(c,0,sizeof(c));
memset(d,0,sizeof(d));
cnt=0;
for(int i=1;i<n;++i){
int x,y;scanf("%d%d",&x,&y);
add(x,y),add(y,x);
++d[x],++d[y];
}
for(int i=1;i<=n;++i)
if(d[i]==1&&!vis[i]) del_chain(i);
for(int x=1;x<=n;++x)
for(int i=head[x];i;i=e[i].nxt) c[x]+=vis[e[i].to];
bool can=1;
for(int x=1;x<=n;++x){
int cc=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
if(d[y]>3 || (d[y]==3&&c[y]-vis[x]<2) || (d[y]==2&&c[y]-vis[x]<1)) ++cc;
}
if(cc>=3){
can=0;
break;
}
}
puts(can?"Yes":"No");
}
return 0;
}