今天他们在外面校庆,我们在机房考试,qwq
T1:排兵布阵(BJOI2019)
这道题有个很明显的策略就是,每个城堡要么放要么不放,而且放的话必须达到最小的那个值
∗
2
+
1
*2+1
∗2+1,所以我们在读入时即可先处理,然后由于题目中有个
s
=
1
s=1
s=1我们可以联想到
01
01
01背包,然后写完
40
p
t
s
40pts
40pts以后,我们可以继续联想到组合背包(虽然我没学过 ),将每个城堡中每个人派遣的人数排序,然后就是一个赤裸裸的背包了,算贡献时
∗
i
*i
∗i即可。
代码:
#include<bits/stdc++.h>
#define re register
#define db double
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int f[20005];
int s,n,m,a[105][105];
int main()
{
s=read();
n=read();
m=read();
for(re int i=1;i<=s;++i)
for(re int j=1;j<=n;++j) a[j][i]=read();
for(re int i=1;i<=n;++i)
{
sort(a[i]+1,a[i]+1+s);
for(re int j=m;j;--j)
for(re int k=1;2*a[i][k]+1<=j&&k<=s;++k)
{
f[j]=max(f[j],f[j-1-2*a[i][k]]+i*k);
}
}
printf("%d",f[m]);
return 0;
}
T2:小X的二叉树
这道题正解是 O ( n l o g n ) O(nlogn) O(nlogn)的复杂度,而我用 O ( n ) O(n) O(n)的贪心搞了过去,实乃人生之大幸。
由于是中序遍历,所以先左子树,根节点,最后右子树,所以我们可以想到,在这个长度为 n n n的序列中,必有一个根节点,并且,在这个序列中,它是除了它自身以外所有点的祖先,而在它的左侧的是左子树,右侧的是右子树,我们分治下去即可,每产生一个区间就判断是否合法即可。
但是这样很明显是
O
(
n
²
)
O(n²)
O(n²)的复杂度,不能
A
C
AC
AC,所以这时候我们可以用玄学的启发式合并,从左右两端同时向中间扫,满足条件就递归下去,就成了
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)的复杂度,我不会证 ,但简略地说就是通过一个小的区间,确定一个大的区间。
然后用线段树维护最大最小值即可。
代码:
#include<bits/stdc++.h>
#define db double
#define re register
#define cs const
#define N 2000005
#define mid (l+r)/2
using namespace std;
inline int read()
{
int x=0,f=1;
char ch;
while(ch>'9'||ch<'0')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=x*10+ch-'0';
ch=getchar();
}
return f*x;
}
int T,n,k,val[N];
struct node
{
int maxx,minn;
}tree[N*4];
void build(int k,int l,int r)
{
if(l==r)
{
tree[k].minn=val[l];
tree[k].maxx=val[l];
return;
}
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
tree[k].minn=min(tree[k<<1].minn,tree[k<<1|1].minn);
tree[k].maxx=max(tree[k<<1].maxx,tree[k<<1|1].maxx);
}
void query(int k,int l,int r,int x,int y,int &mi,int &ma)
{
if(x<=l&&r<=y)
{
mi=tree[k].minn;
ma=tree[k].maxx;
return;
}
int mii=0x3f3f3f3f;
int maa=0;
if(x<=mid) query(k<<1,l,mid,x,y,mii,maa);
if(y>mid)
{
query(k<<1|1,mid+1,r,x,y,mi,ma);
mi=min(mi,mii);
ma=max(ma,maa);
return;
}
mi=mii;
ma=maa;
}
bool solve(int l,int r)
{
if(l>r||l==r) return true;
int ll=l,rr=r;
int mi,ma;
query(1,1,n,l,r,mi,ma);
while(ll<=rr)
{
if(val[ll]-mi<=k&&ma-val[ll]<=k) return solve(l,ll-1)&&solve(ll+1,r);
if(val[rr]-mi<=k&&ma-val[rr]<=k) return solve(l,rr-1)&&solve(rr+1,r);
++ll;
--rr;
}
return false;
}
int main()
{
int size=40<<20;
__asm__ ("movq %0,%%rsp\n"::"r"((char*)malloc(size)+size));
T=read();
while(T--)
{
n=read();
k=read();
for(re int i=1;i<=n;++i) val[i]=read();
build(1,1,n);
if(solve(1,n)) puts("Yes");
else puts("No");
}
exit(0);
}
T3:特技飞行(GXOI2019)
这道题涉及了很多知识点。
首先:我们 a , b a,b a,b和 c c c是可以分开考虑的,所以我们先处理 a 和 b a和b a和b,题目要求我们求出最大最小值,且飞机到达终点后必须满足在起点的相对顺序,所以 a 和 b a和b a和b谁大,我们就尽量做这个动作。
- a > b a>b a>b,我们尽量做 a a a,所以我们就所有的逆序对轮换一遍即可。
- a < b a<b a<b,我们尽量做 b b b,交换次数为 n − n- n−置换数,因为只将每个环中的数交换一遍即可,而每个环是独立的,每个环交换 l e n − 1 len-1 len−1次即可。
接下来,我们考虑 c c c的问题,而这个斜着的四边形我们看起来很不舒服,所以我们就曼哈顿转切比雪夫,再用扫描线算出其贡献即可。
最后由于交点数 < 5 e 5 <5e5 <5e5所以我们可以暴力求出,直接用解析式即可。
代码咕咕咕