2202.11.04题解
T1 打地鼠 LuoguP2484
分析
看一眼数据范围,选择暴力;首先在输入每个洞的鼠鼠数量时,统计一个鼠鼠总数 s u m sum sum,
从 ( n , m ) (n,m) (n,m)向 ( 1 , 1 ) (1,1) (1,1)枚举每个 r r r和 c c c,特判$sum\mod (r \times c) \ne 0 $时,即一定不能打完鼠鼠;
无特判时则开始打鼠鼠,一个区域一个区域的遍历,只要有一个洞的鼠鼠数量小于 0 0 0,则直接跳出
知道遇到有所有洞内的鼠鼠全部被打成 0 0 0个时,直接输出答案,因为是从大到小枚举锤子,则此时最优
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll re(){
ll s=0,w=1;char c=getchar();
while(!isdigit(c)){if(c=='-')w=-w;c=getchar();}
while(isdigit(c)){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return s*w;
}
ll Min(ll x,ll y){
return (x<y)?x:y;
}
int n,m;
ll ds[101][101];
ll dog[101][101];
ll sum,ans;
bool pd;
ll killthedog(int r,int c){
ll cnt=1ll*c*r;
if(cnt==1){pd=1;return sum;}
if(sum%cnt!=0){pd=1;return sum;}
ll answ=sum/cnt;
pd=0;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
dog[i][j]=ds[i][j];
for(int i=1;i<=m-r+1;++i){
for(int j=1;j<=n-c+1;++j){
if(dog[i][j]){
for(int o=i+r-1;o>=i;--o){
for(int p=j+c-1;p>=j;--p){
dog[o][p]-=dog[i][j];
if(dog[o][p]<0){pd=1;break;}
}if(pd)break;
}
}if(pd)break;
}if(pd)break;
}
for(int i=1;i<=m;++i){
for(int j=1;j<=n;++j)
if(dog[i][j]){pd=1;break;}
if(pd)break;
}
return answ;
}
int main()
{
m=re();n=re();ans=0x3f3f3f3f;
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
ds[i][j]=re();
sum+=ds[i][j];
}
}
for(int i=m;i>=1;i--){
for(int j=n;j>=1;j--){
ans=killthedog(i,j);
if(!pd)break;
}
if(!pd)break;
}
printf("%lld\n",ans);
return 0;
}
T2 消防LuoguP2491
分析
很容易看出枢纽必须在树的直径上才能获得最优解,因为要获得端点,所以选择用两遍 d f s dfs dfs处理树,需要记录路径,方便后面的对答案的求取;求出端点之后记录其中一个为起点,然后在起点处设置两个指针 i i i和 j j j,在树的直径上前进, i i i自动变化, j j j则在 d i s [ j ] − d i s [ i ] > s dis[j]-dis[i]>s dis[j]−dis[i]>s时才移动,处理出直径上的答案;但是不能只考虑直径上的点,还要考虑直径上的点的子树中的点到枢纽的距离,我们可以用一个 l i n e line line数组来表示当前直径,然后对子树上节点更新答案,选择其中最大的最小值即可
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
ll re(){
ll s=0,w=1;char c=getchar();
while(!isdigit(c)){if(c=='-')w=-w;c=getchar();}
while(isdigit(c)){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return s*w;
}
const int D=5e5+114;
int he[D<<1],nex[D<<1],dis[D<<1],to[D<<1],cnt;
void add(int u,int v,int w){
to[++cnt]=v;dis[cnt]=w;nex[cnt]=he[u];he[u]=cnt;
}
int n,s,ans,k,top;
int d[D];
int fa[D],line[D];
void dfs(int u,int f){//处理直径
fa[u]=f;
if(d[u]>d[k])k=u;
for(int i=he[u];i;i=nex[i]){
int v=to[i];
if(v!=f&&!line[v]){
d[v]=d[u]+dis[i];
dfs(v,u);
}
}
}
signed main()
{
n=re();s=re();ans=0x3f3f3f3f;
for(int i=1;i<n;i++){
int u,v,w;
u=re();v=re();w=re();
add(u,v,w);add(v,u,w);
}
d[1]=1;dfs(1,-1);
d[k]=0;dfs(k,-1);//处理直径
top=k;//记录起点(即直径上一端点)
for(int i=top,j=top;i;i=fa[i]){
while(d[j]-d[i]>s)j=fa[j];
ans=min(ans,max(d[i],d[top]-d[j]));//首先更新在直径上的答案
}
for(int i=top;i;i=fa[i])line[i]=1;
for(int i=top;i;i=fa[i]){
k=i;d[k]=0;//对直径上的节点的子树更新答案
dfs(i,fa[i]);
}
for(int i=1;i<=n;i++)ans=max(ans,d[i]);//统计答案
printf("%d\n",ans);
return 0;
}
T3 染色LuoguP2486
分析
一眼树剖,然后用线段树维护颜色区间,线段树每个节点需要维护左右位置及左右的色块,加上当前节点代表的色块数量,每次在向上修改时,若两个相邻区间,左边的最右色块颜色与右边的最左色块颜色相同,则需要将两区间合并后区间的色块数量减一
然后是路径求和,我们发现树剖求LCA是利用两个点按照深度向上跳最后跳到一起的方法
自下而上,根据深度交替向上跳
而这个路径可以看成‘人’字型
我们不妨把这个路径分成两边左边和右边我们再用变量pos1表示当前要往上跳的路径\上次的终点颜色
pos2表示另一条路径\上一次的终点颜色
这样,如果当前往上跳的路径\这次的起点颜色等于当前要往上跳的路径\上次的终点颜色那么颜色段数量-1
如果当前要往上跳的节点所在路径发生了改变(也就是路径发生了交替),就交换量位置
然后问题就落到如何找起点终点颜色了
很简单,起点颜色就是线段树上查询的左端点的颜色,终点颜色就是查询的右端点的颜色。
在往上跳(线段树查询)的时候顺便记录一下Lc和Rc即可
#include<bits/stdc++.h>
#define L(x) (x<<1)
#define R(x) (x<<1|1)
using namespace std;
typedef long long ll;
ll re(){
ll s=0,w=1;char c=getchar();
while(!isdigit(c)){if(c=='-')w=-w;c=getchar();}
while(isdigit(c)){s=(s<<1)+(s<<3)+(c^48);c=getchar();}
return s*w;
}
const int D=1e5+50;
int n,m,tot,cnt,Lc,Rc;
int w[D],he[D],sz[D],dep[D],fa[D],son[D],top[D],pos[D];
int to[D<<1],nex[D<<1];
struct node{
int l,r;
int val,tag,lc,rc;
}tr[D<<2];
void init(){tot=cnt=0;memset(he,-1,sizeof he);}
void add(int u,int v){to[tot]=v;nex[tot]=he[u];he[u]=tot++;}
void dfs1(int u,int pre,int depth){
sz[u]=1;fa[u]=pre;son[u]=0;dep[u]=depth;
for(int i=he[u];i!=-1;i=nex[i]){
int v=to[i];
if(v==pre)continue;
dfs1(v,u,depth+1);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v])son[u]=v;
}
}
void dfs2(int u,int tp){
pos[u]=++cnt;top[u]=tp;
if(son[u]!=0)dfs2(son[u],top[u]);
for(int i=he[u];i!=-1;i=nex[i]){
int v=to[i];
if(v==fa[u]||v==son[u])continue;
dfs2(v,v);
}
}
void push_down(int rt){
if(tr[rt].tag){
tr[L(rt)].tag=tr[R(rt)].tag=tr[rt].tag;
tr[L(rt)].val=tr[R(rt)].val=1;
tr[L(rt)].lc=tr[L(rt)].rc=tr[rt].lc;
tr[R(rt)].lc=tr[R(rt)].rc=tr[rt].lc;
tr[rt].tag=0;
}
}
void push_up(int rt){
tr[rt].lc=tr[L(rt)].lc;
tr[rt].rc=tr[R(rt)].rc;
int ans=tr[L(rt)].val+tr[R(rt)].val;
if(tr[L(rt)].rc == tr[R(rt)].lc)ans--;
tr[rt].val=ans;
}
void build(int rt,int l,int r){
tr[rt].l=l;tr[rt].r=r;tr[rt].val=0;
if(l==r)return ;
int mid=(l+r)>>1;
build(L(rt),l,mid);build(R(rt),mid+1,r);
}
void update(int rt,int l,int r,int x){
if(tr[rt].l == l && tr[rt].r == r){
tr[rt].val=tr[rt].tag=1;
tr[rt].lc=tr[rt].rc=x;
return ;
}
push_down(rt);
int mid=(tr[rt].l+tr[rt].r)>>1;
if(r<=mid)update(L(rt),l,r,x);
else if(l>mid)update(R(rt),l,r,x);
else{update(L(rt),l,mid,x);update(R(rt),mid+1,r,x);}
push_up(rt);
}
int ask(int rt,int l,int r,int L,int R){
if(tr[rt].l==L)Lc=tr[rt].lc;
if(tr[rt].r==R)Rc=tr[rt].rc;
if(tr[rt].l==l&&tr[rt].r==r)return tr[rt].val;
push_down(rt);
int mid=(tr[rt].l+tr[rt].r)>>1;
if(r<=mid)return ask(L(rt),l,r,L,R);
else if(l>mid)return ask(R(rt),l,r,L,R);
else{
int ans=ask(L(rt),l,mid,L,R)+ask(R(rt),mid+1,r,L,R);
if(tr[L(rt)].rc==tr[R(rt)].lc)ans--;
return ans;
}
push_up(rt);
}
int sb(int u,int v,int id,int c){
int ans=0;
if(id==1){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
update(1,pos[top[u]],pos[u],c);
u=fa[top[u]];
}
if(dep[u]>dep[v])swap(u,v);
update(1,pos[u],pos[v],c);
}
else{
int ans1=-1,ans2=-1;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){swap(u,v);swap(ans1,ans2);}
ans+=ask(1,pos[top[u]],pos[u],pos[top[u]],pos[u]);
if(Rc==ans1)ans--;
ans1=Lc;u=fa[top[u]];
}
if(dep[u]<dep[v]){swap(u,v);swap(ans1,ans2);}
ans+=ask(1,pos[v],pos[u],pos[v],pos[u]);
if(Rc==ans1)ans--;
if(Lc==ans2)ans--;
}
return ans;
}
int main(){
n=re();m=re();
init();
for(int i=1;i<=n;i++)w[i]=re();
for(int i=1;i<n;i++){
int u,v;
u=re();v=re();
add(u,v);add(v,u);
}
dfs1(1,1,1);dfs2(1,1);build(1,1,n);
for(int i=1;i<=n;i++)update(1,pos[i],pos[i],w[i]);
while(m--){
char op[10];
scanf("%s",op);
int u,v,c;
if(op[0]=='C'){
u=re();v=re();c=re();
sb(u,v,1,c);
}
else{
u=re();v=re();
int dog=sb(u,v,2,0);
printf("%d\n",dog);
}
}
return 0;
}