2202.11.04题解

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;  
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值