先来一波反思
T1 没有注意到早餐肠和晚餐肠随机选择其中分别绝对值最小的,导致以为每次从所有的肠里选绝对值最小的,于是开心的码了一波代码,光荣gg。
总结 以后做题一定要好好看题目细节描述,千万不要想当然就写代码,不然会死很惨
T2 当时想到了负负得正,根据符号来判断在哪里加括号的,可惜DP太菜并没有清楚地找到转移方程看了题解才恍然大悟,我果然是太菜了
总结 对于美妙毒瘤的DP一定要多做题,最好在草稿纸上多画几个例子分析分析,状态转移方程最好把概念理清楚再写,不然会把自己搞死
T3 首先暴露了自己对于STL库中各种自带数据结构的使用极其不熟练这个缺点,线段树我也是菜的一* 以后一定要多写写(数据结构不好真的很吃亏啊啊啊啊,毕竟中国是数据结构强国)。当时读题其实读的很清楚,但是写着写着心态莫名崩塌导致细节写晕,送的分都要不起。。心态问题还是要多锻炼。
总结 再一次强调草稿纸的重要性,多画画图举例子不就很清楚了吗QAQ,虽然我当时一开始用例子确实理解清楚题意了,但是做着做着就把题忘了可能这就是zz吧,要多读几次题,搞清细节,不能慌,仔细分析题意,把该拿的分拿到,保证不出现zz过失。
以上就是此次考试的反思,希望以后的反思能越来越简洁,不要再犯这么多zz错误了,调整好状态,我可以!!!
T1
思路
Tom每天会随机吃一顿饭,可能是早饭,也可能是晚饭。如果是吃早饭,Tom会吃掉编号绝对值最小的早餐肠,反之吃掉编号绝对值最小的晚餐肠。 这句话仿佛在暗示我们早餐肠和晚餐肠要分开标号
如下图:如果Tom今天想吃早饭了吃了个②,那这棵树就凉了。
于是我们得到了一个思路:
正数和负数编号各自组成了一个联通块,并且-b和+a必须相邻(吃绝对值最小or最大)
做法:我们找到一条边,使这条边一端是以-b为根的子树,一条边是以+a为根的子树,然后dfs向下按照dfs序标号即可
上代码
#include <bits/stdc++.h>
using namespace std;
const int N=(int)1e5+500;
int t,n,a,b,num1,num2;
int siz[N],id[N];
int fa[N];
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
int fir[N],nxt[N<<1],tot,to[N<<1];
void add(int x,int y){
nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;
}
void gsize(int x,int f){
siz[x]=1,fa[x]=f;
for(int i=fir[x];i;i=nxt[i]){
if(to[i]==f) continue;
gsize(to[i],x);siz[x]+=siz[to[i]];
}
}
void color(int x,int fa,int *val,int del){
for(int i=fir[x];i;i=nxt[i]){
if(to[i]!=fa){
color(to[i],x,val,del);
}
}
(*val)+=del;//指针的妙用:方便直接修改值,传递地址
id[x]=(*val);
}
int main(){
n=read();num1=read(),num2=read();
for(int i=1;i<n;i++){
a=read(),b=read();
add(a,b);add(b,a);
}
gsize(1,0);
bool ok=0;
for(int i=1;i<=n;i++){
if(siz[i]==num1){
int x=0;
color(i,fa[i],&x,1);//正着向下染一棵a树
x=0;
color(fa[i],i,&x,-1);//反着向上染一棵b树
ok=1;
}
if(siz[i]==num2){
int x=0;
color(i,fa[i],&x,-1);//此处用了指针,方便修改值
x=0;
color(fa[i],i,&x,1);
ok=1;
}
}
if(!ok) return puts("-1"),0;
for(int i=1;i<=n;i++){
printf("%d ",id[i]);
}
printf("\n");
return 0;
}
注意
可能根节点在哪个子树中(或是哪个子树的根),而它的子树大小不是num1或者num2,故处理完子树大小开始染色时要对num1和num2都进行处理,而不是只处理一个就完了。
ps:因为我们每次染色都使用指针重新赋值,不用担心最后值出现问题。
T2
提交地址
有一条浅显的性质:负负得正
于是得到本题思路:考虑在每一个负号前是添加括号更有还是不添加负号更优,得到dp方程
设seq表示当前序列的数,
f
i
,
j
f_{i,j}
fi,j表示,当前到了序列的第
i
i
i位,还有
j
j
j个左括号没有被匹配。
因为在
+
+
+前补括号相当于没补,所以我们只探讨在负号前补括号的情况,由负负得正考虑符号反向,使加的数最大即可最优
注意:处理时记得
s
e
q
[
i
]
seq[i]
seq[i]是一个带符号的数
如果当前数
>
0
>0
>0(符号为正)
f
[
i
]
[
0
]
=
m
a
x
(
f
[
i
−
1
]
[
0
]
+
s
e
q
[
i
]
,
f
[
i
−
1
]
[
1
]
+
s
e
q
[
i
]
,
f
[
i
−
1
]
[
2
]
+
s
e
q
[
i
]
)
;
f[i][0]=max(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);
f[i][0]=max(f[i−1][0]+seq[i],f[i−1][1]+seq[i],f[i−1][2]+seq[i]);
f
[
i
]
[
1
]
=
m
a
x
(
f
[
i
−
1
]
[
1
]
−
s
e
q
[
i
]
,
f
[
i
−
1
]
[
2
]
−
s
e
q
[
i
]
)
;
f[i][1]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);
f[i][1]=max(f[i−1][1]−seq[i],f[i−1][2]−seq[i]);
f
[
i
]
[
2
]
=
f
[
i
−
1
]
[
2
]
+
s
e
q
[
i
]
;
f[i][2]=f[i-1][2]+seq[i];
f[i][2]=f[i−1][2]+seq[i];
如果当前数
<
0
<0
<0(符号为负)
f
[
i
]
[
0
]
=
−
I
N
F
;
/
/
这
种
情
况
不
可
能
存
在
,
故
转
移
决
策
时
不
可
能
取
到
f[i][0]=-INF;//这种情况不可能存在,故转移决策时不可能取到
f[i][0]=−INF;//这种情况不可能存在,故转移决策时不可能取到
f
[
i
]
[
1
]
=
m
a
x
(
f
[
i
−
1
]
[
0
]
+
s
e
q
[
i
]
,
f
[
i
−
1
]
[
1
]
+
s
e
q
[
i
]
,
f
[
i
−
1
]
[
2
]
+
s
e
q
[
i
]
)
;
/
/
不
管
怎
么
样
那
个
数
该
减
还
是
要
被
减
f[i][1]=max(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);//不管怎么样那个数该减还是要被减
f[i][1]=max(f[i−1][0]+seq[i],f[i−1][1]+seq[i],f[i−1][2]+seq[i]);//不管怎么样那个数该减还是要被减
f
[
i
]
[
2
]
=
m
a
x
(
f
[
i
−
1
]
[
1
]
−
s
e
q
[
i
]
,
f
[
i
−
1
]
[
2
]
−
s
e
q
[
i
]
)
;
/
/
负
负
得
正
f[i][2]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);//负负得正
f[i][2]=max(f[i−1][1]−seq[i],f[i−1][2]−seq[i]);//负负得正
代码
#include <bits/stdc++.h>
#define int long long
#define maxn(x,y,z) max(max(x,y),z)
using namespace std;
const int N=500050;
int n,seq[N],f[N][4],t;
int INF=(int)-1e18;
inline int read(){
int cnt=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-') f=-f;c=getchar();}
while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c^48);c=getchar();}
return cnt*f;
}
signed main(){
t=read();
while(t--){
n=read();
for(int i=1;i<=n;i++){
seq[i]=read();
}
f[0][0]=0,f[0][1]=f[0][2]=INF;
for(int i=1;i<=n;i++){
if(seq[i]>0){
f[i][0]=maxn(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);
f[i][1]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);
f[i][2]=f[i-1][2]+seq[i];
}
else{
f[i][0]=INF;
f[i][1]=maxn(f[i-1][0]+seq[i],f[i-1][1]+seq[i],f[i-1][2]+seq[i]);
f[i][2]=max(f[i-1][1]-seq[i],f[i-1][2]-seq[i]);
}
}
printf("%lld\n",maxn(f[n][0],f[n][1],f[n][2]));
}
return 0;
}
T3
提交地址
手玩几组样例,发现
x
x
x坐标是单调不减的,且我们只用维护矩形的左边界(因为
x
x
x不走回头路,路径直接加上
X
t
X_t
Xt即可),路径要拐弯只可能在矩形边界处拐弯(达到最优),所以我们考虑维护当前所处位置
X
i
,
[
l
,
r
]
X_i,[l,r]
Xi,[l,r]的
y
y
y坐标上的前驱(最小的最大)和后继(最大的最小),中间因为反正也不考虑走进矩形,直接不管。
思路:考虑维护每个
X
i
X_i
Xi区间的
l
,
r
l,r
l,r,放进一个
s
e
t
set
set里,
(
l
,
r
)
(l,r)
(l,r)的值都标为
I
N
F
INF
INF,只维护区间端点就可以了。
代码(抄的,我咕了我过几天再自己写代码)
#include <bits/stdc++.h>
using namespace std;
const int MX = 500005;
const int oo = 1000000000;
template <typename T> void read(T &x)
{
x = 0; char c = getchar(); bool f = 0;
while(!isdigit(c) && c!='-') c = getchar();
if(c == '-') f = 1, c = getchar();
while(isdigit(c)) x = x*10+c-'0', c = getchar();
if(f) x = -x;
}
struct NODE
{
int x, y, d;
NODE (const int &x0 = 0, const int &y0 = 0, const int &d0 = 0) : x(x0), y(y0), d(d0) {}
bool operator < (const NODE &t) const {return y < t.y;}
};
struct REC
{
int l, r, d, u;
REC (const int &l0 = 0, const int &r0 = 0, const int &d0 = 0, const int &u0 = 0) : l(l0), r(r0), d(d0), u(u0) {}
void adjust()
{
if(l > r) swap(l, r);
if(d > u) swap(d, u);
}
bool operator < (const REC &t) {return l < t.l;}
};
int n, tx, ty;
REC obs[MX];
void input()
{
read(n);
read(tx), ty = 0;
for(int i=1; i<=n; i++)
{
read(obs[i].l), read(obs[i].d);
read(obs[i].r), read(obs[i].u);
obs[i].adjust();
}
}
int dis(int x1, int y1, int x2, int y2)
{
return abs(x1-x2) + abs(y1-y2);
}
int calc()
{
set<NODE> st;
sort(obs+1, obs+n+1);
st.insert(NODE(0, 0, 0));
for(int i=1; i<=n; i++)
{
set<NODE>::iterator itl, itr, del;
int mnd = +oo, mnu = +oo;
itl = st.lower_bound(NODE(obs[i].l, obs[i].d, 0));
itr = st.upper_bound(NODE(obs[i].l, obs[i].u, 0));
while(itl != itr)
{
del = itl;
mnd = min(mnd, dis(del->x, del->y, obs[i].l, obs[i].d) + del->d);
mnu = min(mnu, dis(del->x, del->y, obs[i].l, obs[i].u) + del->d);
itl++;
st.erase(del);
}
st.insert(NODE(obs[i].l, obs[i].d, mnd));
st.insert(NODE(obs[i].l, obs[i].u, mnu));
}
int ret = +oo;
for(set<NODE>::iterator it = st.begin(); it!=st.end(); it++)
ret = min(ret, dis(it->x, it->y, tx, ty) + it->d);
cerr<<ret<<endl;
return ret;
}
void work()
{
printf("%d\n", calc());
}
int main()
{
freopen("speike.in", "r", stdin);
freopen("speike.out", "w", stdout);
input();
work();
return 0;
}