T1

显然,对于一个城堡,要么就不放兵,要么就放到\(2*a_i+1\)

然后对于一个城堡,我们首先将对手放兵从小到大排序,然后就是一个分组背包(每一组只能选1个)的问题了QAQ 复杂度\(O(nms)\) 因为跑不满上界,所以过得了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=1100;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int s,n,m;
int f[maxn][20010];
int dp[20010],qaq[maxn];
int arr[maxn][maxn],arrr[maxn][maxn],val[maxn][maxn];
int main(){
	freopen("arrange.in","r",stdin);
	freopen("arrange.out","w",stdout);
	s=read();n=read();m=read();
	for(int i=1;i<=s;i++)
		for(int j=1,x;j<=n;j++)
			x=read(),arr[i][j]=x*2+1;
	if(s==1){
		for(int i=1;i<=n;i++){
			for(int j=0;j<=m;j++)
				f[i][j]=f[i-1][j];
			for(int j=arr[1][i];j<=m;j++){
				f[i][j]=max(f[i][j],f[i-1][j-arr[1][i]]+i);
			}
		}	
		printf("%d",f[n][m]);
	}
	else{
		memset(dp,0xcf,sizeof(dp));
		dp[0]=0;
		for(int i=1;i<=s;i++)
			for(int j=1;j<=n;j++)
				arrr[j][i]=arr[i][j];
		for(int i=1;i<=n;i++)
			sort(arrr[i]+1,arrr[i]+1+s);
		for(int i=1;i<=n;i++){
			for(int j=m;j>=0;j--){
				for(int k=1;k<=s;k++){
					if(j>=arrr[i][k])
						dp[j]=max(dp[j],dp[j-arrr[i][k]]+(i)*(k));
				}
			}
		}

		int ans=0;
		for(int i=0;i<=m;i++) ans=max(ans,dp[i]);
		printf("%d",ans);
	}
	return 0;
}

T2

这道题通过题目可以分析出来:父亲与其所有的后代权值的差绝对值\(\le k\) 。我们考虑先找一个根节点:显然根节点与除了自己的所有节点的权值差绝对值\(\le k\) 找到根节点之后,我们把根节点抛开不管,显然根节点的左子树在中序遍历中一定在根节点的左边,右子树在中序遍历中一定在根节点的右边,然后将根节点的左子树,右子树(看做独立的树) 进行同样的操作,最后就可以构成符合条件的树,如何不能构成,就返回\(false\)

如何做思路就很明了了

\(solve(l,r)\) 表示当前处理到中序遍历的\([l,r]\)了,然后在这个区间从左向右扫一遍,找到一个满足条件的\(i\)使得\(a[i]+k>=maxx \&\& a[i]-k<=maxx\) 然后继续递归\(solve(l,i-1),solve(i+1,r)\)

但是显然这样做肯定是\(O(n^2)\)的 过不了。 然而玄学的启发式合并可以将这道题的复杂度转换为\(O(nlogn)\),我不会证。 启发式合并就是从当前枚举的左右区间同时向中间扫 即在第i步的时候判断\(l+i,r-i\) 对应的数是否是满足条件的。如果满足条件就可以直接递归下去。 至于为什么启发式合并要快一点,因为同时从左边,右边向中间扫,而如果扫到了就可以直接递归下去。相当于可以通过一个小的区间,确定一个大的区间,复杂度自然就下去了

还有就是注意ST表维护区间最大最小值的细节QAQ

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;
const int maxn=2e5+10;
int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int t,n,k,a[maxn],lg[maxn],stmin[30][maxn],stmax[30][maxn];
int getmin(int l,int r){
	int k=lg[r-l+1];
	return min(stmin[k][l],stmin[k][r-(1<<k)+1]);
}
int getmax(int l,int r){
	int k=lg[r-l+1];
	return max(stmax[k][l],stmax[k][r-(1<<k)+1]);
}
bool solve(int l,int r){
	if(l>=r) return true;
	int minn=getmin(l,r),maxx=getmax(l,r);
	int i=0;
	for(;i+l<=r-i;i++){
		if(a[i+l]-k<=minn && a[i+l]+k>=maxx){
			return solve(l,l+i-1) && solve(l+i+1,r);
		}
		if(a[r-i]-k<=minn && a[r-i]+k>=maxx){
			return solve(l,r-i-1) && solve(r-i+1,r);
		}
	}
	return false;
}
int main(){
	freopen("tree2.in","r",stdin);
	freopen("tree.out","w",stdout);
	t=read();
	while(t--){
		n=read();k=read();
		lg[0]=-1;
		for(int i=1;i<=n;i++){
			a[i]=read();
			lg[i]=lg[i>>1]+1;
			stmin[0][i]=a[i];stmax[0][i]=a[i];
		}
		for(int i=1;i<=lg[n];i++)
			for(int j=1;j+(1<<i)-1<=n;j++){
				stmin[i][j]=min(stmin[i-1][j],stmin[i-1][j+(1<<(i-1))]);
				stmax[i][j]=max(stmax[i-1][j],stmax[i-1][j+(1<<(i-1))]);
			}
		solve(1,n)?printf("Yes\n"):printf("No\n");
	}
	return 0;
}
posted on 2019-11-09 17:07  萌德真帅  阅读(103)  评论(0编辑  收藏  举报