20221104

20221104

susliks

传送门

思路

这道题第一眼看去并没有什么思路。但暴力的思路还是很好想的,枚举 \(r,c\) 然后每次暴力枚举敲击区域的左上角位置,很容易发现,如果我们敲击的位置有 \(i\) 只地鼠,那我们就必须在这个位置上敲击 \(i\) 次,所以我们只需要给敲击区域上的所有点减去 \(i\) 然后判断是否可行即可。 (然后考试的时候上了个厕所就忘了) 然后再考虑如何继续优化这个暴力,可以发现每次敲击都必定会打掉 \(r\times c\) 只地鼠,换言之,每次敲击都可以使地鼠总量减少 \(r\times c\) ,所以可以打掉所有地鼠,并且每次都必须打掉 \(r\times c\) 只地鼠的锤子大小 \(r\times c\) 必然是总地鼠数的约数。这样就可以将原本 \(O(n^6)\) 的暴力优化到 \(O(n^4k)\)\(k\) 为总地鼠数的约数个数的组合方案数),当然原本的 \(O(n^6)\) 中本来就有 \(n^2\) 其实是 \(r\times c\) 。然后差不多就能过了。(然而枚举 \(r,c\) 的时候出问题了,直接白给 100pts )

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
    x=0;int f=0;char c=getchar();
    for(;!isdigit(c);c=getchar())f|=(c=='-');
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x=f?~x+1:x;
}
template<typename T>inline void out(T x){
    if(x<0)x=~x+1,putchar('-');
    if(x>9)out(x/10);
    putchar(x%10^48);
}
const int N=105;
int m,n;
int mp[N][N],gg[N][N];
ll sum,ans;
inline bool check(int x,int y){
    fo(i,1,m)
	fo(j,1,n)gg[i][j]=mp[i][j];
    fo(i,1,m-x+1){
	fo(j,1,n-y+1){
	    if(gg[i][j]){
		int z=gg[i][j];
		for(int k=0;k<x;k++){
		    for(int l=0;l<y;l++){
			gg[i+k][j+l]-=z;
			if(gg[i+k][j+l]<0)return 0;
		    }
		}
	    }
	}
    }
    fo(i,1,m)
	fo(j,1,n)
	    if(gg[i][j])return 0;
    return 1;
}
int main(){
    in(m),in(n);
    fo(i,1,m){
	fo(j,1,n){
	    in(mp[i][j]);
	    sum+=mp[i][j];
	}
    }
    ans=sum;
    for(int i=m;i>=1;--i){
	for(int j=n;j>=1;--j){
	    if(sum%(i*j)==0&&sum/i/j<ans){
		if(check(i,j)){
		    ans=sum/i/j;
		}
	    }	
	}
    }
    out(ans);
    return 0;
}

fire

传送门

前言

考试的时候什么都想出来了,就是最后统计答案的时候挂了,又白送出题人 \(100pts\) 。当然也可能是因为时间分配不均的原因,打到最后统计答案的时候就只剩下 \(10\) 分钟了,在第一题浪费的时间过于多了,以后一定要严格控制每道题时间。

思考

如果做过与树的直径有关的题,应该很容易想到与树的直径有关。这里我们可以小小证明一下。

树的直径

假设 \(1,2,3,4\) 即路径 \(1\) -> \(4\) 是树的直径上的点,如果我们选择的路径不在树的直径上,假设我们选择不在树的直径上的路径 \(4,3,2,6,8\) 即路径 \(4\) -> \(8\) ,因为 \(1\) 是树的直径上的点,所以 \(2\)\(1\) 的距离一定大于 \(2\)\(8\) 的距离,所以选择路径 \(4\) -> \(8\) 剩余的最大距离就是 \(2\)\(1\) 的距离,而选择树的直径剩余的最大距离就是 \(2\)\(8\) 的距离。但是 \(2\)\(1\) 的距离又大于 \(2\)\(8\) 的距离,所以选树的直径上的点一定更优。

统计答案

找到树的直径后统计答案就很简单了。很容易想到枚举直径上的两个距离不超过 \(s\) 的端点,然后得到左端点左边部分与右端点右边部分,还有左右端点之内最长的子树三者之中的最大值即可。对于直径上的最长长度可以用前缀和预处理出来。而对于直径上的点的子树的最长长度也可以用 \(dp\) 预处理出来。最后,因为是前缀和,其实对于每个点我们都可以直接二分找到相距不超过 \(s\) 的最远的点。这样,统计答案的时间复杂度就优化到 \(O(nlogn)\) 的了。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
    x=0;int f=0;char c=getchar();
    for(;!isdigit(c);c=getchar())f|=(c=='-');
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x=f?~x+1:x;
}
template<typename T>inline void out(T x){
    if(x<0)x=~x+1,putchar('-');
    if(x>9)out(x/10);
    putchar(x%10^48);
}
inline int maX(int a,int b){
    return a>b?a:b;
}
const int N=3e5+5;
int n,s;
struct edge{
    int v,nex,w;
}e[N<<1];
int head[N],tot;
inline void add(int u,int v,int w){e[++tot].v=v,e[tot].w=w,e[tot].nex=head[u],head[u]=tot;}
ll dis[N];
int st,ed,maxnn;
inline void dfs(int x,int fa){//求树的直径
    if(dis[x]>maxnn){
        ed=x;
	maxnn=dis[x];
    }
    for(int i=head[x];i;i=e[i].nex){
	int v=e[i].v;
	if(v==fa)continue;
	dis[v]=dis[x]+e[i].w;
	dfs(v,x);
    }
}
int pre[N],nex[N];//类似链表,用于剖出直径
bool f;
int gg[N];//按照原编号记录的前缀和
inline void dfs_cl(int x,int fa){//将直径剖出
    if(f)return;
    if(x==ed){
	f=1;
	return;
    }
    for(int i=head[x];i;i=e[i].nex){
	int v=e[i].v;
	if(v==fa)continue;
	if(f)return;
	pre[v]=x;
	nex[x]=v;
	gg[v]=gg[x]+e[i].w;
	dfs_cl(v,x);
    }
}
int dfn[N],bf[N],lon[N],cnt;
//dfn为按顺序编号后的直径上的点的编号,bf为编号的好之前的编号,lon为按原编号记录的子树最长深度编号
inline void DFS(int x,int fa,int root){//求直径上每个点的最长子树长度
    for(int i=head[x];i;i=e[i].nex){
	int v=e[i].v;
	if(v==fa||v==nex[root]||v==pre[root])continue;
	DFS(v,x,root);
	lon[x]=maX(lon[x],lon[v]+e[i].w);
    }
}
int len[N];//编好号之后的最长子树深度
int F[N][21];
inline void ST_prwork(){
    fo(i,1,cnt)F[i][0]=len[i];
    int t=log(n)/log(2)+1;
    fo(j,1,t-1)
	fo(i,1,n-(1<<j)+1)
	    F[i][j]=max(F[i][j-1],F[i+(1<<(j-1))][j-1]);
    return;
}
inline int ST_query(int l,int r){
    int k=log(r-l+1)/log(2);
    return max(F[l][k],F[r-(1<<k)+1][k]);
}
int gerp[N];//编号号后的前缀和
int main(){
    in(n),in(s);
    int a,b,c;
    pre[1]=nex[1]=1;
    fo(i,2,n){
	in(a),in(b),in(c);
	add(a,b,c);
	add(b,a,c);
	pre[i]=nex[i]=i;
    }
    dfs(1,1);
    st=ed;
    maxnn=0;
    memset(dis,0,sizeof(dis));
    dfs(st,st);//求树的直径
    dfs_cl(st,st);//剖出直径
    int now=st;
    while(nex[now]!=now){//重新编号
	dfn[now]=++cnt;
	bf[cnt]=now;
	now=nex[now];
    }
    dfn[now]=++cnt;
    bf[cnt]=now;
    fo(i,1,cnt)DFS(bf[i],bf[i],bf[i]);//最长子树
    fo(i,1,cnt)gerp[i]=gg[bf[i]],len[i]=lon[bf[i]];
    int ans=0x3f3f3f3f;
    ST_prwork();
    fo(i,1,cnt){
	int pp=upper_bound(gerp+i,gerp+cnt+1,s+gerp[i])-gerp-1;
	ans=min(ans,max(max(gerp[i],gerp[cnt]-gerp[pp]),ST_query(i,pp)));
    }
    out(ans);
    return 0;
}

dye

传送门

思路

一眼树链剖分。只需要在线段树中多记录一个左端点和右端点的颜色,再在合并的时候判断是否相同,相同就减一即可。

咳,貌似写得太简单了,补充一下。。。

线段树合并

对于这道题,线段树合并的操作稍有不同。

对于这样的两个区间,左区间的颜色段个数是 \(3\) ,右区间的颜色段数是 \(2\) ,直接相加得到 \(5\) ,但实际上是 \(4\) ,其实就是因为它们合并时中间段的颜色相同,导致黑色段在左边被算了一次,还在右边被算了一次,总共就被多算了1次。所以当两区间合并时相接部分颜色相同时答案减一即可。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define fo(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
template<typename T>inline void in(T &x){
    x=0;int f=0;char c=getchar();
    for(;!isdigit(c);c=getchar())f|=(c=='-');
    for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+(c^48);
    x=f?~x+1:x;
}
template<typename T>inline void out(T x){
    if(x<0)x=~x+1,putchar('-');
    if(x>9)out(x/10);
    putchar(x%10^48);
}
const int N=100005<<1;
int n,m;
int w[N];
struct edge{
    int v,nex;
}e[N<<1];
int head[N],tot;
inline void add(int u,int v){e[++tot].v=v,e[tot].nex=head[u],head[u]=tot;}
int fa[N],dep[N],size[N],wson[N];
inline void dfs(int x){
    size[x]=1;
    for(int i=head[x];i;i=e[i].nex){
	int v=e[i].v;
	if(v==fa[x])continue;
	fa[v]=x;
	dep[v]=dep[x]+1;
	dfs(v);
	size[x]+=size[v];
	if(size[v]>size[wson[x]])wson[x]=v;
    }
}
int top[N],dfn[N],cnt,pre[N];
inline void dfs_tp(int x,int topf){
    dfn[x]=++cnt,pre[cnt]=x,top[x]=topf;
    if(wson[x])dfs_tp(wson[x],topf);
    for(int i=head[x];i;i=e[i].nex){
	int v=e[i].v;
	if(v==wson[x]||v==fa[x])continue;
	dfs_tp(v,v);
    }
}
struct node{
    int l,r,lch,rch,val,tag;
    int lc,rc;
    node(){
	tag=0;
    }
}rt[N<<1];
int tote;
#define ls rt[x].lch
#define rs rt[x].rch
inline void push_up(int x){
    rt[x].val=rt[ls].val+rt[rs].val;
    if(rt[ls].rc==rt[rs].lc)--rt[x].val;
    rt[x].lc=rt[ls].lc;
    rt[x].rc=rt[rs].rc;
    return ;
}
inline int build(int l,int r){
    ++tote;
    int x=tote;
    rt[x].l=l,rt[x].r=r;
    if(l==r){
	rt[x].val=1;
	rt[x].lc=w[pre[l]];
	rt[x].rc=w[pre[l]];
	return x;
    }
    int mid=l+r>>1;
    rt[x].lch=build(l,mid);
    rt[x].rch=build(mid+1,r);
    push_up(x);
    return x;
}
inline void push_down(int x){
    if(rt[x].tag!=1)return;
    rt[ls].val=rt[rs].val=1;
    rt[ls].lc=rt[ls].rc=rt[rs].lc=rt[rs].rc=rt[x].lc;
    rt[x].tag=0;
    rt[ls].tag=rt[rs].tag=1;
    return;
}
inline void modify(int x,int l,int r,int v){
    if(l<=rt[x].l&&rt[x].r<=r){
	rt[x].val=1;
	rt[x].lc=v;
	rt[x].rc=v;
	rt[x].tag=1;
	return;
    }
    int mid=rt[x].l+rt[x].r>>1;
    push_down(x);
    if(l<=mid)modify(ls,l,r,v);
    if(r>mid)modify(rs,l,r,v);
    push_up(x);
    return;
}
inline int query(int x,int l,int r){
    if(l<=rt[x].l&&rt[x].r<=r)return rt[x].val;
    int mid=rt[x].l+rt[x].r>>1;
    int tmp=0;
    push_down(x);
    if(l<=mid)tmp+=query(ls,l,r);
    if(r>mid){
	if(tmp>0&&rt[ls].rc==rt[rs].lc)--tmp;
	tmp+=query(rs,l,r);
    }
    push_up(x);
    return tmp;
}
inline int queryc(int x,int pos){
    if(rt[x].l==rt[x].r)return rt[x].lc;
    int mid=rt[x].l+rt[x].r>>1;
    int tmp;
    push_down(x);
    if(pos<=mid)tmp=queryc(ls,pos);
    else tmp=queryc(rs,pos);
    push_up(x);
    return tmp;
}
#undef ls
#undef rs
inline void lcam(int x,int y,int v){
    while(top[x]!=top[y]){
	if(dep[top[x]]<dep[top[y]])swap(x,y);
	modify(1,dfn[top[x]],dfn[x],v);
	x=fa[top[x]];
    }
    if(dep[x]<dep[y])swap(x,y);
    modify(1,dfn[y],dfn[x],v);
    return;
}
inline int lcaq(int x,int y){
    int ans=0;
    while(top[x]!=top[y]){
	if(dep[top[x]]<dep[top[y]])swap(x,y);
	ans+=query(1,dfn[top[x]],dfn[x]);
	if(queryc(1,dfn[top[x]])==queryc(1,dfn[fa[top[x]]]))--ans;
	x=fa[top[x]];
    }
    if(dep[x]<dep[y])swap(x,y);
    ans+=query(1,dfn[y],dfn[x]);
    return ans;
}
int main(){
    in(n),in(m);
    fo(i,1,n)in(w[i]);
    int u,v;
    fo(i,2,n){
	in(u),in(v);
	add(u,v);
	add(v,u);
    }
    fa[1]=1;
    dfs(1);
    dfs_tp(1,1);
    build(1,n);
    char Op;
    int a,b,c;
    fo(i,1,m){
	cin>>Op;
	if(Op=='C'){
	    in(a),in(b),in(c);
	    lcam(a,b,c);
	}
	else{
	    in(a),in(b);
	    out(lcaq(a,b)),putchar('\n');
	}
    }
    return 0;
}
posted @ 2022-11-04 21:05  リン・グァン  阅读(14)  评论(0编辑  收藏  举报