2021-09-27(含上下午)

AM

预期一测 Δ \Delta Δ
Larget Rectangle in a Histogram1001000
没有上司的舞会1001000
阶乘分解1001000
欧拉回路3015-15
括号配对1001000
总分430415-15

Largest Rectangle in a Histogram

单调栈板题。

先简化思路对于矩形都单调递增,很明显以每一个矩形的高为最高高度,将宽度延伸到右边界,这样得到的所有矩形中最大的就是答案。如果下一个矩形高度比上一个小,那么该前面矩形的高度就都没用了,为何不删掉(弹出)前面比当前矩形高的矩形,用一个宽度为删掉(弹出)矩形的宽度累和加上当前矩形的宽度,高度为当前矩形的一个新矩形代替(入栈)。

在弹出矩形时,将弹出矩形的高度乘上已累积的宽度,就可以更新答案,为了把所有矩形都弹出,最后我们可以将一个高度为 0 的矩形入栈。

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define ll long long
int n,a[N],s[N],r,w[N];
int main(){
	while(cin>>n && n){
		ll ans=0;
		for(int i=1;i<=n;++i){
			scanf("%d",a+i);
		}
		a[n+1]=r=0;
		for(int i=1;i<=n+1;++i){
			if(a[i]>s[r]){
				s[++r]=a[i],w[r]=1;
			} else {
				int wid=0;
				while(s[r]>a[i]){
					wid+=w[r];
					ans=max(ans,(ll)wid*s[r--]);
				}
				s[++r]=a[i],w[r]=++wid;
			}
		}
		printf("%lld\n",ans);
	}
	return 0;
}

没有上司的舞会

树形 D P DP DP板题。设 F [ x , 0 ] F[x,0] F[x,0]表示以 x x x为根的子树不邀请 x x x的情况, F [ x , 1 ] F[x,1] F[x,1]表示以 x x x为根的子树邀请 x x x的情况。为了方便设 G [ x ] G[x] G[x]表示以 x x x为根的子树的最优情况。可得·
F [ x , 0 ] = ∑ y ∈ S o n ( x ) G [ y ] F [ x , 1 ] = H [ x ] + ∑ y ∈ S o n ( x ) F [ y , 0 ] G [ x ] = m a x { F [ x , 0 ] , F [ x , 1 ] } F[x,0]=\sum_{y\in Son(x)}G[y]\\ F[x,1]=H[x]+\sum_{y\in Son(x)}F[y,0]\\ G[x]=max\{F[x,0],F[x,1]\} F[x,0]=ySon(x)G[y]F[x,1]=H[x]+ySon(x)F[y,0]G[x]=max{F[x,0],F[x,1]}
G [ r o o t ] G[root] G[root]即是答案。

#include<bits/stdc++.h>
using namespace std;
#define N 6010
int root,n,l,k,h[N];
int head[N],nxt[N],to[N],cnt;
int g[N],f[N][2];
int fa[N];
void add(int u,int v){
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
}
void dfs(int x){
	for(int i=head[x];i;i=nxt[i]){
		int y=to[i];
		dfs(y);
		f[x][0]+=g[y];
		f[x][1]+=f[y][0];
	}
	g[x]=max(f[x][0],f[x][1]+h[x]);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",h+i);
		fa[i]=i;
	}
	for(int i=1;i<=n;++i){
		scanf("%d%d",&l,&k);
		fa[l]=k;
		add(k,l);
	}
	for(int i=1;i<=n;++i){
		if(fa[i]==i){
			root=i;
			break;
		}
	}
	dfs(root);
	printf("%d",g[root]);
	return 0;
}

阶乘分解

显然, N ! N! N!的每个质因子都不会超过 N N N,先筛出 1   N 1~N 1 N的每个质数 p p p,然后考虑阶乘 N ! N! N!中一共包含多少个 p p p
1~ N N N中,至少包含1个 p p p的显然有 ⌊ N / p ⌋ \lfloor N/p \rfloor N/p个。至少包含1个 p 2 p^{2} p2的显然有 ⌊ N / p 2 ⌋ \lfloor N/p^{2} \rfloor N/p2个。不过其中一个质因子已经在 ⌊ N / p ⌋ \lfloor N/p \rfloor N/p中统计过,所以只需加上 ⌊ N / p 2 ⌋ \lfloor N/p^{2} \rfloor N/p2个,而不是 2 × ⌊ N / p 2 ⌋ 2\times\lfloor N/p^{2} \rfloor 2×N/p2个。综上, N ! N! N!中质因子 p p p的个数为:
⌊ N p ⌋ + ⌊ N p 2 ⌋ + ⌊ N p 3 ⌋ + ⋯ + ⌊ N p ⌊ log ⁡ p N ⌋ ⌋ = ∑ p k ≤ N ⌊ N p k ⌋ \lfloor \frac{N}{p} \rfloor+\lfloor \frac{N}{p^{2}} \rfloor+\lfloor \frac{N}{p^{3}} \rfloor+\cdots+\lfloor \frac{N}{p^{\lfloor\log_{p}{N} \rfloor}} \rfloor=\sum_{p^{k}\leq{N}}\lfloor\frac{N}{p^{k}} \rfloor pN+p2N+p3N++plogpNN=pkNpkN

#include<bits/stdc++.h>
using namespace std;
#define N 1000010
int v[N],p[N],m;
void pre(int n){
	for(int i=2;i<=n;++i){
		if(!v[i]) p[++m]=i,v[i]=i;
		for(int j=1;j<=m;++j){
			if(p[j]>v[i] || p[j]>n/i) break;
			v[i*p[j]]=p[j];
		}
	}
}
int main(){
	int n;
	scanf("%d",&n);
	pre(n);
	for(int i=1;i<=m;++i){
		int k=0,g=p[i];
		for(int j=n;j;j/=g) k+=j/g;
		printf("%d %d\n",p[i],k);
	}
	return 0;
}

括号配对

区间 D P DP DP,设 F [ l , r ] F[l,r] F[l,r]表示 l l l r r r 需要添几个括号,易得
F [ l , r ] = 1    ( l = = r ) F [ l , r ] = m i n { F [ l + 1 , r − 1 ] ( a [ l ] 与 a [ r ] 相 互 匹 配 ) , min ⁡ l ≤ k < r ( F [ l , k ] , F [ k + 1 ] [ r ] ) } F[l,r]=1\ \ (l==r) \\ F[l,r]=min\{F[l+1,r-1](a[l]与a[r]相互匹配),\min_{l \leq k<r}(F[l,k],F[k+1][r]) \} F[l,r]=1  (l==r)F[l,r]=min{F[l+1,r1](a[l]a[r]),lk<rmin(F[l,k],F[k+1][r])}

#include<bits/stdc++.h>
using namespace std;
#define N 110
int dp[N][N];
bool v[N][N];
char a[N];
int dfs(int l,int r){
	if(v[l][r]) return dp[l][r];
	v[l][r]=1;
	if(r==l)return dp[l][r]=1;
	if(a[l]=='[' && a[r]==']' || a[l]=='(' && a[r]==')'){
		if(r==l+1) return dp[l][r]=0;
		dp[l][r]=dfs(l+1,r-1);
	}
	for(int i=l;i<r;++i)
		dp[l][r]=min(dp[l][r],dfs(l,i)+dfs(i+1,r));
	return dp[l][r];
}
int main(){
	scanf("%s",a+1);
	memset(dp,0x3f,sizeof(dp));
	printf("%d\n",dfs(1,strlen(a+1)));
	
}

PM

预期一测 Δ \Delta Δ
完全平方回文数100
均分纸牌100
打击犯罪100
农场派对100
树的统计100
总分500

完全平方回文数

暴力

#include<bits/stdc++.h>
using namespace std;
#define N 111
int l,r,u,v,ans;
bool check(int x,int y){
	int s[40],tot=0;
	while(x){
		s[++tot]=x%y;
		x/=y;
	}
	for(int i=1;i<=tot/2;++i)
		if(s[i]!=s[tot-i+1]) return 0;
	return 1;
}
int main(){
	scanf("%d%d%d%d",&l,&r,&u,&v);
	for(int i=l;i<=r;++i){
		int flag=0;
		for(int j=u;j<=v;++j){
			if(check(i*i,j)) flag++;
			if(flag==2) break;
		}
		if(flag==1) ans++;
	}
	printf("%d",ans);
	return 0;
}

均分纸牌

将所有数减去平均数,从左到右,若当前牌刚好为 0 就不管,不是就向右边的借,答案累加 1 。

#include<bits/stdc++.h>
using namespace std;
#define N 111
int a[N],n,s,ans;
int main(){
	int n;
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%d",a+i);
		s+=a[i];
	}
	s/=n;
	for(int i=1;i<=n;++i) a[i]-=s;
	for(int i=1;i<=n;++i){
		if(a[i]==0) continue;
		a[i+1]+=a[i];
		ans++;
	}
	printf("%d",ans);
	return 0;
}

打击犯罪

由于它是从 1~k 依次打击,直接二分答案 k 用并查集检查是否合法。

#include<bits/stdc++.h>
using namespace std;
#define N 1010
bool p[N][N];
int fa[N],sum[N],n;
int get(int x){return fa[x]==x ? x : fa[x]=get(fa[x]);}
bool check(int mid){
	for(int i=mid+1;i<=n;++i) fa[i]=i,sum[i]=1;
	for(int i=mid+1,x;i<=n;++i)
		for(int j=i+1,y;j<=n;++j){
			if(p[i][j]){
				x=get(i),y=get(j);
				if(x==y) continue;
				sum[x]+=sum[y];
				if(sum[x]>n/2) return 0;
				fa[y]=x;
			}
		}
	return 1;
}
int main(){
	scanf("%d",&n);
	for(int i=1,k,x;i<=n;++i){
		scanf("%d",&k);
		while(k--) scanf("%d",&x),p[i][x]=p[x][i]=1; 
	}
	int le=0,ri=n/2+1;
	while(le<ri){
		int mid=(le+ri)>>1;
		if(check(mid)) ri=mid;
		else le=mid+1;
	}
	printf("%d",le);
	return 0;
}

农场派对

首先对于一头牛来说过去和回来的路不会互相影响,就分开计算,回来跑一便 d i j k s t r a dijkstra dijkstra,那过去怎么办?直接建反图再跑一遍 d i j k s t r a dijkstra dijkstra就行了。
由于此题 n n n只有1000,用不用堆优化都行。好像堆优化跟n2差qiu不多
堆优化:

#include<bits/stdc++.h>
using namespace std;
#define mkp make_pair
#define X first
#define Y second
#define N 1010
priority_queue<pair<int,int> > q;
int head[N],nxt[N*100],len[N*100],to[N*100],cnt,d[N];
int fhead[N],fnxt[N*100],flen[N*100],fto[N*100],fcnt,fd[N];
bool v[N],fv[N];
int n,m,k,ans;
void add(int x,int y,int z){to[++cnt]=y;len[cnt]=z;nxt[cnt]=head[x];head[x]=cnt;}
void fadd(int x,int y,int z){fto[++fcnt]=y;flen[fcnt]=z;fnxt[fcnt]=fhead[x];fhead[x]=fcnt;}
void dj(){
	memset(d,0x3f,sizeof(d));
	d[k]=ans=0;
	q.push(mkp(0,k));
	while(q.size()){
		int x=q.top().Y;q.pop();
		if(v[x]) continue;
		v[x]=1;
		for(int i=head[x];i;i=nxt[i]){
			int y=to[i],z=len[i];
			if(d[y]>d[x]+z){
				d[y]=d[x]+z;
				q.push(mkp(-d[y],y));
			}
		}
	}
	for(int i=1;i<=n;++i) cout<<d[i]<<" ";
}
void fdj(){
	memset(fd,0x3f,sizeof(fd));
	fd[k]=ans=0;
	q.push(mkp(0,k));
	while(q.size()){
		int x=q.top().Y;q.pop();
		if(fv[x]) continue;
		fv[x]=1;
		for(int i=fhead[x];i;i=fnxt[i]){
			int y=fto[i],z=flen[i];
			if(fd[y]>fd[x]+z){
				fd[y]=fd[x]+z;
				q.push(mkp(-fd[y],y));
			}
		}
	}
	for(int i=1;i<=n;++i) cout<<fd[i]<<" ";
	for(int i=1;i<=n;++i) ans=max(ans,d[i]+fd[i]);
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z),fadd(y,x,z);
	}
	dj();
	puts("");
	fdj();
	printf("%d",ans);
}

朴素:

#include<bits/stdc++.h>
using namespace std;
#define N 1001
int a[N][N],fa[N][N],d[N],fd[N],n,m,k,ans;
bool v[N],fv[N];
void dj(){
	memset(d,0x3f,sizeof(d));
	memset(fd,0x3f,sizeof(fd));
	d[k]=fd[k]=0;
	for(int i=1,x;i<n;++i){
		x=0;
		for(int j=1;j<=n;++j) if(!v[j] && (x==0 || d[j]<d[x])) x=j;
		v[x]=1;
		for(int y=1;y<=n;++y) d[y]=min(d[y],d[x]+a[x][y]);
		x=0;
		for(int j=1;j<=n;++j) if(!fv[j] && (x==0 || fd[j]<fd[x])) x=j;
		fv[x]=1;
		for(int y=1;y<=n;++y) fd[y]=min(fd[y],fd[x]+fa[x][y]);	
	}
	for(int i=1;i<=n;++i) ans=max(ans,d[i]+fd[i]);
}
int main(){
	memset(a,0x3f,sizeof(a));
	memset(fa,0x3f,sizeof(fa));
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;++i){
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		a[x][y]=min(a[x][y],z);
		fa[y][x]=min(fa[y][x],z);
	}
	dj();
	printf("%d",ans);
}

树的统计

树剖板子

#include<bits/stdc++.h>
#define N 1000009
#define LL long long
#define ql q<<1
#define qr q<<1|1
using namespace std;
template<typename T>inline void read(T &x){
	T ch=getchar(),xx=1;x=0;
	while(!isdigit(ch)) xx=ch=='-' ? -1 : xx,ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	x*=xx;
}
template<typename T>inline void print(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) print(x/10);
	putchar(x%10+'0');
}
int n,cnt,m;
int head[N],to[N<<1],nxt[N<<1];
int w[N],f[N],dep[N],W[N];
int fa[N],top[N],siz[N],son[N];
int seg[N],rev[N],tot;
struct seg_tree{
	int l,r;
	LL maxx,sum;
}t[N<<2];
void build(int q,int l,int r){
	t[q].l=l,t[q].r=r;
	if(l==r){
		t[q].maxx=t[q].sum=w[rev[l]];
		return ;
	}
	int mid=(l+r)>>1;
	build(ql,l,mid);
	build(qr,mid+1,r);
	t[q].maxx=max(t[ql].maxx,t[qr].maxx);
	t[q].sum=t[ql].sum+t[qr].sum;
}
void change(int q,int u,int v) {
	if(t[q].l==t[q].r){ 
		t[q].maxx=t[q].sum=v; 
		return;
	}
	int mid=(t[q].l+t[q].r)>>1;
	if (u<=mid) change(ql,u,v); 
	else change(qr,u,v);
	t[q].maxx=max(t[ql].maxx,t[qr].maxx);
	t[q].sum=t[ql].sum+t[qr].sum;
}
LL ask1(int q,int l,int r) {
	if (l>t[q].r || r<t[q].l || l>r) return -(1<<30);
	if (l<=t[q].l && r>=t[q].r) return t[q].maxx;
	return max(ask1(ql,l,r),ask1(qr,l,r));
}
LL ask2(int q,int l,int r) {
	if (l>t[q].r || r<t[q].l || l>r) return 0;
	if (l<=t[q].l && r>=t[q].r) return t[q].sum;
	return ask2(ql,l,r)+ask2(qr,l,r);
}
inline void add(int u,int v){to[++cnt]=v,nxt[cnt]=head[u],head[u]=cnt;}
void dfs1(int u,int h){
	dep[u]=dep[h]+1;
	siz[u]=1;
	fa[u]=h;
	for(int i=head[u];i;i=nxt[i]){
		int v=to[i];
		if(v==h) continue;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])
			son[u]=v;
	}
}
void dfs2(int x){
	top[x]=(x==son[fa[x]]? top[fa[x]] : x);
	seg[x]=++tot;
	rev[tot]=x;
	if(son[x])
		dfs2(son[x]);
	for(int i=head[x];i;i=nxt[i]){
		int v=to[i];
		if(!top[v])
			dfs2(v);
	}
}
LL ask_sum(int x,int y){
	LL ret=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])
			swap(x,y);
		ret+=ask2(1,seg[top[x]],seg[x]);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	ret+=ask2(1,seg[x],seg[y]);
	return ret;	
}
LL ask_max(int x,int y){
	LL ret=-(1<<30);
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])
			swap(x,y);
		ret=max(ask1(1,seg[top[x]],seg[x]),ret);
		x=fa[top[x]];
	}
	if(dep[x]>dep[y])
		swap(x,y);
	ret=max(ask1(1,seg[x],seg[y]),ret);
	return ret;	
}
char op[8];
int main(){
	read(n);
	for(int i=1;i<n;i++){
		int u,v;
		read(u),read(v);
		add(u,v);
		add(v,u);
	}
	dfs1(1,0);
	top[1]=1;
	dfs2(1);
	for(int i=1;i<=n;i++) read(w[i]);
	build(1,1,n);
	read(m);
	for(int i=1;i<=m;i++){
		scanf("%s",op);
		int u,v;
		read(u),read(v);
		if(op[1]=='H') change(1,seg[u],v);
		if(op[1]=='M') print(ask_max(u,v)),puts("");
		if(op[1]=='S') print(ask_sum(u,v)),puts("");
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值