Hey,Lucida is back.

CS


  • 首页

  • 归档

  • friends

  • 搜索

BZOJ 4723 Flappy Bird

发表于 2017-03-05 | 更新于 2018-06-17

Link

据说是比较水的题目?然而我似乎被NOIP题框住了。。

Solution

维护小鸟能到达的范围。因为是每次只能上下111,所以根据横坐标的差值可以得到纵坐标的变化区间。 一开始是[0,0][0,0][0,0],然后依次扩大,并与可以通过障碍物的区间取交。 注意:可以通过障碍物的区间并不是每个点都可以到达,只有纵坐标值奇偶性和横坐标相同的时候才可以。但是区间之内的点不会造成影响,只有区间端点才会造成影响(区间长度≥3\ge 3≥3的时候,一定存在合法的点)。所以每次要判断一下得到的区间是否合法。 当区间为空集的时候立即退出,因为后面的操作还会再把区间扩大,可能就又合法了。

Tips

做到题目的弱化或者强化版不要被之前的思路限制住,要找到原来的做法套在当前题目上多了什么,少了什么。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//Code by Lucida
#include<bits/stdtr1c++.h>
//#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
typedef long long int64;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
namespace IO {
template <class T> inline void get(T &x) {
static bool f;static char ch;
for(f=0,ch=0;ch<'0' || '9'<ch;ch=getchar()) f|=ch=='-';
for(x=0;'0'<=ch && ch<='9';ch=getchar()) (x*=10)+=ch-'0';
x=f?-x:x;
}
}
using IO::get;
using std::max;
using std::min;
const int MAXN=5e5+11;
struct Block {
int x,a,b;
}block[MAXN];
struct Interval
{
int l,r;
Interval(int l,int r):l(l),r(r){}
bool isEmpty() {
return l>r;
}
friend Interval operator & (const Interval &a,const Interval &b) {
return Interval(max(a.l,b.l),min(a.r,b.r));
}
Interval &operator &= (const Interval &a) {
return *this=*this & a;
}
};
int main() {
int n,goal;get(n),get(goal);
for(int i=1;i<=n;++i) {
get(block[i].x);
get(block[i].a);
get(block[i].b);
}
Interval va(0,0);//(0,0)
for(int i=1;i<=n;++i) {
int d=block[i].x-block[i-1].x;
va.l-=d,va.r+=d;
va&=Interval(block[i].a+1,block[i].b-1);
if((va.l-block[i].x)&1) va.l++;
if((va.r-block[i].x)&1) va.r--;
if(va.isEmpty()) break;//2333???
}
if(va.isEmpty())
puts("NIE");
else
put((block[n].x+va.l)>>1),putchar('\n');
return 0;
}

BZOJ 4347 Nim z utrudnieniem

发表于 2017-03-05 | 更新于 2018-06-17

Link

Solution

问题就是在nnn个数字a[i]a[i]a[i]中选出ddd的倍数个,使得这些数字的 值为给定值,询问方案数目。 直接DP是很显然的:f[i][j][k]f[i][j][k]f[i][j][k]表示算到了第iii个数字,取的数字 值为jjj,取了的个数 的方案数目。 方程为 发现卡空间,可以直接把这个方程用010101背包的方法原地转移。 发现TLE,那就把a[]a[]a[]排个序,每次只需要处理a[i]<<1a[i]<<1a[i]<<1个数字,总复杂度O(Md)O(Md)O(Md)。 看到Claris大神给了另一个DP: 方程相应为 这样确实就需要用那种神奇的方法了……这个神奇的方法确实很厉害,应该能在别的地方用到,但我还是觉得对这道题来说我的方法好一些。

Tips

如果给定一个∑<x\sum < x∑<x的条件,要对这个条件思考一下能不能用来优化。另一个例子就是SDOI的那道虚树DP。 的题目排序之后就可以让 的值域单调,也许能有些用处。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <lucida>
using std::sort;
const int MAXN=500000+11,MAXS=1048576+11,P=1e9+7;
int main() {
freopen("input","r",stdin);
static int f[10][MAXS],g[MAXS],a[MAXN];
int xr=0,m=0,n,d;
is>>n>>d;
for(int i=1;i<=n;++i) {
is>>a[i];
xr^=a[i];
m+=a[i];
}
sort(a+1,a+1+n);
f[0][0]=1;
int lim=1;
for(int i=1;i<=n;++i) {
while(lim<=a[i]) lim<<=1;
for(int k=0;k<=lim;++k) {
g[k]=f[d-1][k];
}
for(int j=d-1;j>=1;--j) {
for(int k=0;k<=lim;++k) {
(f[j][k]+=f[j-1][k^a[i]])%=P;
}
}
for(int k=0;k<=lim;++k) {
(f[0][k]+=g[k^a[i]])%=P;
}
}
int Ans=n%d?f[0][xr]:(f[0][xr]-1+P)%P;
os<<Ans<<'\n';
return 0;
}
//plus 另一种状态表示.

BZOJ 4727 Turysta

发表于 2017-03-05 | 更新于 2018-06-18

Link

题意是每个点至多经过一次,“没有重复经过同一个点两次以上的简单路径”感觉怪怪的。

Solution

这是一张竞赛图 这样的问题肯定是要缩点DP的,问题是如何构造路径。

竞赛图

两点之间有且只有一条有向边 结论来了:强连通的竞赛图一定有蛤密顿回路

首先要证明任意竞赛图都有蛤密顿路径,因为需要用来构造蛤密顿回路 n==2n==2n==2,结论成立 设n=kn=kn=k,结论成立,需要证明在n=k+1n=k+1n=k+1时结论也成立 对于原有的路径P=V1V2V3...VkP=V_1V_2V_3...V_kP=V​1​​V​2​​V​3​​...V​k​​ 如果存在<Vi,Vk+1>,<Vk+1,Vi+1>< V_i,V_{k+1} >,< V_{k+1},V_{i+1} ><V​i​​,V​k+1​​>,<V​k+1​​,V​i+1​​>,那么存在。 否则, 如果有<Vk+1,V1>< V_{k+1},V_1 ><V​k+1​​,V​1​​>或者<Vk,Vk+1>< V_k,V_{k+1} ><V​k​​,V​k+1​​>,那么存在。 假设上面的情况都不符合,那么有<V1,Vk+1>< V_1,V_{k+1} ><V​1​​,V​k+1​​>,<Vk+1,Vk>< V_{k+1},V_k ><V​k+1​​,V​k​​>,中间的点无论怎么连边都会有一个转折点,就符合了第一种情况,所以假设不成立。 所以结论成立。

关于一些扩展内容

有了蛤密顿路,来构造蛤密顿回路,主要思路是扩展已有的环 对于蛤密顿路径PPP的前kkk个点组成的环CkC_kC​k​​,现在要加入Vk+1V_{k+1}V​k+1​​得到Ck+1C_{k+1}C​k+1​​ (实现的时候把环拆开,维护断点,设为首尾)

  1. 存在<Vk+1,V1>< V_{k+1},V_1 ><V​k+1​​,V​1​​>,直接加入,把环尾设置为Vk+1V_{k+1}V​k+1​​
  2. 存在<Vi,Vk+1>,<Vk+1,Vi+1>< V_i,V_{k+1} >,< V_{k+1},V_{i+1} ><V​i​​,V​k+1​​>,<V​k+1​​,V​i+1​​>,先把环首尾接起来,然后在Vi+1V_{i+1}V​i+1​​处把环拆开,把VkV_kV​k​​接在ViV_iV​i​​上,把首尾设置为Vi+1,Vk+1V_{i+1},V_{k+1}V​i+1​​,V​k+1​​
  3. 以上都不存在,那么肯定存在Vh,h>k+1V_h,h> k+1V​h​​,h>k+1有<Vh,Vs>,s≤k< V_h,V_s >,s\le k<V​h​​,V​s​​>,s≤k,否则就不是强连通图了。那就找到这个点,同样先把环接起来,从VsV_sV​s​​处拆环,把Vs−1V_{s-1}V​s−1​​与Vk+1V_{k+1}V​k+1​​连接,把环的首尾设置为Vs,VhV_s,V_hV​s​​,V​h​​
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
int ce=he;Add(he,pts);
while(ce!=ta) {
int cur=hsuc[ce];
if(map[cur][he]) {
Add(cur,pts);
ce=cur;
}
else {
bool found=0;
for(int u=he;u!=ce;u=hsuc[u])
if(map[u][cur] && map[cur][hsuc[u]]) {
hsuc[ce]=he;
he=hsuc[u];
hsuc[u]=cur;
ce=cur;
found=1;
break;
}

if(!found) {
int boss,brp,brpred=0;
for(int u=cur;assert(u),1;u=hsuc[u])
if(reach[u] && !ud[u]) {
boss=u;
break;
}
for(int u=he;assert(u!=cur),1;brpred=u,u=hsuc[u])
if(map[boss][u]) {
brp=u;
break;
}
brpred=brpred==0?ce:brpred;
hsuc[ce]=he;
hsuc[brpred]=cur;
he=brp;
ce=boss;
for(int u=cur;u!=hsuc[boss];u=hsuc[u])
Add(u,pts);

}
}
}
hsuc[ta]=he;

这样先SCC,然后对于每个SCC都构造蛤密顿回路。 缩点之后,SCC之间的连边也一定是竞赛图的形式,如果有两条有向边,就成了一个SCC。也就是说,如果对于两个SCCS1,S2S_1,S_2S​1​​,S​2​​,存在边<S1,S2>< S_1,S_2 ><S​1​​,S​2​​>,那就一定不存在<S2,S1>< S_2,S_1><S​2​​,S​1​​>,且一定有∀u∈S1,v∈S2\forall u\in S_1,v\in S_2∀u∈S​1​​,v∈S​2​​,存在边<u,v>< u,v ><u,v>。 然后缩点图上进行DP,最后构造答案输出即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
//Code by Lucida
#include<bits/stdtr1c++.h>
typedef long long int64;
namespace IO
{
template <class T> inline void get(T &x) {
static bool f;static char ch;
for(f=0,ch=0;ch<'0' || '9'<ch;ch=getchar()) f|=ch=='-';
for(x=0;'0'<=ch && ch<='9';ch=getchar()) (x*=10)+=ch-'0';
x=f?-x:x;
}
template <> inline void get<char>(char &x) {
while(x=getchar(),((x<'0' || '9'<x) && (x<'a' || 'z'<x) && (x<'A' || 'Z'<x)));
}
template <> inline void get<char*>(char *&x) {
char *cp=x;
while(*cp++=getchar(),(*cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=EOF));
*--cp=0;
}

template <class T> inline void put(T x) {
if(!x)
putchar('0');
else {
if(x<0) {
putchar('-');
x=-x;
}
static char stack[30]={0};
char *top=stack;
for(;x;x/=10) *++top=x%10+'0';
while(*top) putchar(*top--);
}
}
template <> inline void put<char>(char x) {
putchar(x);
}
template <> inline void put<char*>(char* x) {
while(*x)
putchar(*x++);
}
}

template <class T> inline bool chkmx(T &a,const T &b) {
return a<b?a=b,1:0;
}
template <class T> inline bool chkmn(T &a,const T &b) {
return a>b?a=b,1:0;
}

using std::swap;
using std::max;
using std::min;
using IO::get;
using IO::put;


using std::vector;

const int MAXN=2e3+11;
int n,scnt,sn[MAXN],hsuc[MAXN],f[MAXN],dsuc[MAXN];
vector<int> scc[MAXN];
bool map[MAXN][MAXN],cot[MAXN][MAXN];
namespace _Tarjan_ {
int dc,dfn[MAXN],low[MAXN],stack[MAXN],top;
bool instack[MAXN];
void DFS(int pos,int fa) {
instack[stack[++top]=pos]=1;
dfn[pos]=low[pos]=++dc;
for(int u=1;u<=n;++u)
if(map[pos][u] && u!=fa) {
if(!dfn[u]) {
DFS(u,pos);
chkmn(low[pos],low[u]);
} else if(instack[u])
chkmn(low[pos],dfn[u]);
}
if(dfn[pos]==low[pos]) {
++scnt;
while(stack[top+1]!=pos) {
int u=stack[top];
sn[u]=scnt;
instack[u]=0;
scc[scnt].push_back(u);
top--;
}
}
}
void Tarjan() {
for(int i=1;i<=n;++i)
if(!dfn[i])
DFS(i,0);
}
}using _Tarjan_::Tarjan;

namespace _Hamilton_ {
bool ud[MAXN],reach[MAXN];
void Add(int pos,vector<int> &pts) {
ud[pos]=1;
for(unsigned int i=0;i<pts.size();++i)
reach[pts[i]]|=map[pts[i]][pos];
}
void Hamilton(int css) {
vector<int> &pts=scc[css];
int he=pts[0],ta=pts[0],sz=pts.size();
//构造哈密顿路
for(int i=1;i<=sz-1;++i) {
int cur=pts[i];
if(map[cur][he]) {
hsuc[cur]=he;
he=cur;
} else if(map[ta][cur]) {
hsuc[ta]=cur;
ta=cur;
} else {
for(int u=he;assert(u!=ta),1;u=hsuc[u]) {
if(map[u][cur] && map[cur][hsuc[u]]) {
hsuc[cur]=hsuc[u];
hsuc[u]=cur;
break;
}
}
}
}
//变成哈密顿回路
int ce=he;Add(he,pts);
while(ce!=ta) {
int cur=hsuc[ce];
if(map[cur][he]) {
Add(cur,pts);
ce=cur;
}
else {
bool found=0;
for(int u=he;u!=ce;u=hsuc[u])
if(map[u][cur] && map[cur][hsuc[u]]) {
hsuc[ce]=he;
he=hsuc[u];
hsuc[u]=cur;
ce=cur;
found=1;
break;
}

if(!found) {
int boss,brp,brpred=0;
for(int u=cur;assert(u),1;u=hsuc[u])
if(reach[u] && !ud[u]) {
boss=u;
break;
}
for(int u=he;assert(u!=cur),1;brpred=u,u=hsuc[u])
if(map[boss][u]) {
brp=u;
break;
}
brpred=brpred==0?ce:brpred;
hsuc[ce]=he;
hsuc[brpred]=cur;
he=brp;
ce=boss;
for(int u=cur;u!=hsuc[boss];u=hsuc[u])
Add(u,pts);

}
}
}
hsuc[ta]=he;
}
}using _Hamilton_::Hamilton;
void Walk(int pos,int *s,int &cnt) {
s[++cnt]=pos;
for(int u=hsuc[pos];u!=pos;u=hsuc[u])
s[++cnt]=u;
if(dsuc[sn[pos]])
Walk(scc[dsuc[sn[pos]]][0],s,cnt);
}
int DP(int css) {
if(f[css]) return f[css];
for(int c=1;c<=scnt;++c)
if(cot[css][c] && chkmx(f[css],DP(c)))
dsuc[css]=c;
return f[css]+=scc[css].size();
}
int main() {
// freopen("input","r",stdin);
get(n);
for(int i=2;i<=n;++i)
for(int j=1;j<=i-1;++j) {
int b;get(b);
map[j][i]=b;
map[i][j]=b^1;
}
Tarjan();
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
cot[sn[i]][sn[j]]|=(sn[i]!=sn[j] && map[i][j]);
for(int i=1;i<=scnt;++i) {
f[i]=DP(i);
Hamilton(i);
}
static int s[MAXN];

for(int i=1;i<=n;++i) {
int Ans=f[sn[i]];
put(Ans),put(' ');
int cnt=0;
Walk(i,s,cnt);
for(int j=1;j<=cnt;++j) {
put(s[j]),put(j==cnt?'\n':' ');
}
}
/*
for(int i=1;i<=n;++i)
put(f[sn[i]]),putchar('\n');
*/
return 0;
}

BZOJ 4724 Podzielno

发表于 2017-03-03 | 更新于 2018-06-18

Link

Solution

显然, 所以问题就是在这些数字中选出尽量多的数使得和为B−1B-1B−1的倍数,在最多的情况下字典序最大。 我又想DP了,233...似乎跟Bird的思维过程惊人相似。。好吧 因为对于任何数字,有a[i]≥1a[i]\ge 1a[i]≥1,所以肯定至少可以取到∑a[i]−1\sum a[i]-1∑a[i]−1个。而且这样肯定也是最优的。 剩下的数字排个序就是答案要求的数了。。 然后询问在前缀和里面二分就好了(然而这个二分我还费了好大的劲才转过弯来。。)

Tips

std::partial_sum的等价代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
template <class InputIterator, class OutputIterator>
OutputIterator partial_sum (InputIterator first, InputIterator last,
OutputIterator result)
{
if (first!=last) {
typename iterator_traits<InputIterator>::value_type val = *first;
*result = val;
while (++first!=last) {
val = val + *first; // or: val = binary_op(val,*first)
*++result = val;
}
++result;
}
return result;
}

没错,中间值是用输入数据的类型计算的!!不要问我怎么知道的!!

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//Code by Lucida
#include<bits/stdtr1c++.h>
typedef long long int64;
namespace IO
{
template <class T> inline void get(T &x) {
static bool f;static char ch;
for(f=0,ch=0;ch<'0' || '9'<ch;ch=getchar()) f|=ch=='-';
for(x=0;'0'<=ch && ch<='9';ch=getchar()) (x*=10)+=ch-'0';
x=f?-x:x;
}
template <> inline void get<char>(char &x) {
while(x=getchar(),((x<'0' || '9'<x) && (x<'a' || 'z'<x) && (x<'A' || 'Z'<x)));
}
template <> inline void get<char*>(char *&x) {
char *cp=x;
while(*cp++=getchar(),(*cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=EOF));
*--cp=0;
}

template <class T> inline void put(T x) {
if(!x)
putchar('0');
else {
if(x<0) {
putchar('-');
x=-x;
}
static char stack[30]={0};
char *top=stack;
for(;x;x/=10) *++top=x%10+'0';
while(*top) putchar(*top--);
}
}
template <> inline void put<char>(char x) {
putchar(x);
}
template <> inline void put<char*>(char* x) {
while(*x)
putchar(*x++);
}
}

template <class T> inline bool chkmx(T &a,const T &b) {
return a<b?a=b,1:0;
}
template <class T> inline bool chkmn(T &a,const T &b) {
return a>b?a=b,1:0;
}

using std::swap;
using std::max;
using std::min;
using IO::get;
using IO::put;


using std::upper_bound;
using std::partial_sum;

const int MAXN=1e6+11;
int main() {
// freopen("input","r",stdin);
static int64 pref[MAXN],a[MAXN];
int K,Q;get(K),get(Q);
int64 sum=0;
for(int i=0;i<K;++i) {
get(a[i]);
(sum+=(int64)a[i]*i%(K-1))%=(K-1);
}
if(sum!=0) {
a[sum]--;
}
partial_sum(a,a+K,pref);
for(int i=1;i<=Q;++i) {
int64 x;get(x);
int p=upper_bound(pref,pref+K,x)-pref;
p=p==K?-1:p;
put(p),put('\n');
}
return 0;
}

BZOJ 4725 Reprezentacje różnicowe

发表于 2017-03-03 | 更新于 2018-06-18

Link

搜到了波兰人的题解!!还有视频!!

Solution

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  1:                   1
2: 2
3: 4
4: 8
5: 16
6: 21
7: 42
8: 51
9: 102
10: 112
11: 224
12: 235
13: 470
14: 486
15: 972
16: 990
17: 1980
18: 2002
19: 4004
20: 4027
21: 8054
22: 8078
23: 16156
24: 16181
25: 32362
26: 32389
27: 64778
28: 64806
29: 129612
30: 129641
31: 259282
32: 259313
33: 518626
34: 518658
35: 1037316
36: 1037349
37: 2074698
38: 2074734
39: 4149468
40: 4149505
41: 8299010
42: 8299049
43: 16598098
44: 16598140
45: 33196280
46: 33196324
47: 66392648
48: 66392693
49: 132785386
50: 132785432
51: 265570864
52: 265570912
53: 531141824
54: 531141876
55: 1062283752
56: 1062283805
57: 2124567610
58: 2124567664
59: 4249135328
60: 4249135383
61: 8498270766
62: 8498270822
63: 16996541644
64: 16996541701
65: 33993083402
66: 33993083460
67: 67986166920
68: 67986166979
69: 135972333958
70: 135972334020
71: 271944668040
72: 271944668103
73: 543889336206
74: 543889336270
75: 1087778672540
76: 1087778672605
77: 2175557345210
78: 2175557345276
79: 4351114690552
80: 4351114690619
81: 8702229381238
82: 8702229381306
83: 17404458762612
84: 17404458762681
85: 34808917525362
86: 34808917525433
87: 69617835050866
88: 69617835050938
89: 139235670101876
90: 139235670101949
91: 278471340203898
92: 278471340203972
93: 556942680407944
94: 556942680408019
95: 1113885360816038
96: 1113885360816114
97: 2227770721632228
98: 2227770721632305
99: 4455541443264610
100: 4455541443264688

打表发现,50来项之后就有 ,而a[2i+1]=2a[2i]a[2i+1]=2a[2i]a[2i+1]=2a[2i],所以差值要 ,只能是一个偶数项和它的前一项,也就是a[2i]−a[2i−1]=r[2i−1]a[2i]-a[2i-1]=r[2i-1]a[2i]−a[2i−1]=r[2i−1]。 这些a[2i]a[2i]a[2i]只能让集合SSS增加一个 的数字,即r[2i−1]r[2i-1]r[2i−1],所以数组r[]r[]r[]在a[i]>1e9a[i]> 1e9a[i]>1e9之后是连续变化,每次增加111。 所以把小范围的答案算出来存进 ,也把SSS存起来; 大范围xxx的在SSS中二分找到最后一个小于xxx的ppp,也就是说在预处理的这些项之外,要找到第x−px-px−p对数字。然后再算一下就可以得到答案了。 比较好理解,但我太jiruo,想了很长时间。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//Code by Lucida
#include<bits/stdtr1c++.h>
typedef long long int64;
namespace IO
{
template <class T> inline void get(T &x) {
static bool f;static char ch;
for(f=0,ch=0;ch<'0' || '9'<ch;ch=getchar()) f|=ch=='-';
for(x=0;'0'<=ch && ch<='9';ch=getchar()) (x*=10)+=ch-'0';
x=f?-x:x;
}
template <> inline void get<char>(char &x) {
while(x=getchar(),((x<'0' || '9'<x) && (x<'a' || 'z'<x) && (x<'A' || 'Z'<x)));
}
template <> inline void get<char*>(char *&x) {
char *cp=x;
while(*cp++=getchar(),(*cp!=' ' && *cp!='\r' && *cp!='\n' && *cp!=EOF));
*--cp=0;
}

template <class T> inline void put(T x) {
if(!x)
putchar('0');
else {
if(x<0) {
putchar('-');
x=-x;
}
static char stack[30]={0};
char *top=stack;
for(;x;x/=10) *++top=x%10+'0';
while(*top) putchar(*top--);
}
}
template <> inline void put<char>(char x) {
putchar(x);
}
template <> inline void put<const char*>(const char* x) {
while(*x)
putchar(*x++);
}

}

template <class T> inline bool chkmx(T &a,const T &b) {
return a<b?a=b,1:0;
}
template <class T> inline bool chkmn(T &a,const T &b) {
return a>b?a=b,1:0;
}

using std::swap;
using std::max;
using std::min;
using IO::get;
using IO::put;


using std::set;
using std::pair;
using std::make_pair;
using std::map;
using std::upper_bound;
const int N=57,MAXN=N+5;
struct Solver {
map<int,pair<int,int> > rec;
int64 a[MAXN],d[N*N];
int dcnt;
Solver() {
a[0]=0,a[1]=1,a[2]=2;
rec[1]=make_pair(2,1);
for(int i=3;i<=N;++i) {
if(i&1) {
a[i]=a[i-1]*2;
} else {
int mex=1;
while(rec.count(mex)) mex++;
a[i]=a[i-1]+mex;
}
for(int j=1;j<i;++j) {
rec[a[i]-a[j]]=make_pair(i,j);
}
}
dcnt=0;
for(map<int,pair<int,int> >::iterator it=rec.begin();it!=rec.end();++it) {
d[++dcnt]=it->first;
}
}
pair<int,int> operator ()(int x) {
if(rec.count(x))
return rec[x];
else {
int p=upper_bound(d+1,d+1+dcnt,x)-d-1;
return make_pair(N+(x-p)*2-1,N+(x-p)*2-2);
}
}
}Cal;
int main() {
//naive哈哈哈
// freopen("input","r",stdin);
int n;get(n);
for(int i=1;i<=n;++i) {
int x;get(x);
pair<int,int> Ans=Cal(x);
put(Ans.first),put(' '),put(Ans.second),put('\n');
}
return 0;
}

uoj 125 书法家

发表于 2017-03-02 | 更新于 2018-06-18

Link

Solution

第一步转化是最重要的:把所有的图形都转化为纵向的几部分,然后分11种情况转移即可 细节不算很多,而且样例给的比较良心 唯一想写一下的是NNN的第2种转移。 大体是这样的形式: f(i,l,r)=min{f(i−1,p,q)∣p∈[l,r+1],q∈[r,n]}+Sum(i,l,r)f(i,l,r)=\min \{f(i-1,p,q)|p\in [l,r+1],q\in[r,n]\}+Sum(i,l,r)f(i,l,r)=min{f(i−1,p,q)∣p∈[l,r+1],q∈[r,n]}+Sum(i,l,r) 这个转移做到O(1)O(1)O(1)对我这种juruo还是比较有难度的。。 想了很长时间都不对,只好看Fuxey神的题解,虽然没有看懂,但得到了一个很好的思路:数形结合 于是用几何画板画出来。 (最后面划的那几下是表明以此类推。。) 然后就很清晰了,只要维护每一竖列的最大值,就可以更新了。

Tips

涉及到不等关系的各种东西,如果想不明白,就画图,画几个连续变化的图感受一下。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define Reset(x) memset(x,170,sizeof(x))
typedef long long int64;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::max;
using std::min;
using std::partial_sum;
const int MAXN=150+11,MAXM=500+11,INF=-0xAAAAAAAA;
int a[MAXM][MAXN],pref[MAXM][MAXN],n,m;
int Sum(int x,int l,int r){return pref[x][r]-pref[x][l-1];}
int Pool[2][MAXM][MAXN][MAXN],Me=0,(*f)[MAXN][MAXN],(*pre)[MAXN][MAXN],g[3][MAXM];
void Alloc(){pre=f;f=Pool[Me^=1];Reset(Pool[Me]);}
void N()
{
Alloc();
for(int i=1;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(f[i-1][l][r],0)+Sum(i,l,r);
Alloc();
for(int i=2;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(pre[i-1][l-1][r]+Sum(i,l,r),f[i][l-1][r]-a[i][l-1]);

int *mx=g[0];
for(int i=3;i<=m;++i)
{
Reset(g[0]);
for(int r=n;r>=1;--r)
{
for(int l=r,maxv=mx[r+1];l>=1;--l)
{
chkmx(mx[l],f[i-1][l][r]);
chkmx(maxv,mx[l]);
chkmx(f[i][l][r],maxv+Sum(i,l,r));
}
}
}
Alloc();
for(int i=3;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(pre[i-1][l][r-1]+Sum(i,l,r),f[i][l][r-1]+a[i][r]);
for(int i=4;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
chkmx(f[i][l][r],f[i-1][l][r]+Sum(i,l,r));
Reset(g[0]);
for(int i=3;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
chkmx(g[0][i],f[i][l][r]);
}
void O()
{
Alloc();
for(int i=5;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(g[0][i-2],f[i-1][l][r]-Sum(i-1,l,r))+Sum(i,l,r);
Alloc();
for(int i=6;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l+2;r<=n;++r)
f[i][l][r]=max(pre[i-1][l][r],f[i-1][l][r])+a[i][l]+a[i][r];
Alloc();
for(int i=7;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=pre[i-1][l][r]+Sum(i,l,r);
Reset(g[1]);
for(int i=7;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
chkmx(g[1][i],f[i][l][r]);

}
void I()
{
Alloc();
for(int i=9;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l+2;r<=n;++r)
f[i][l][r]=max(g[1][i-2],f[i-1][l][r]-a[i-1][l]-a[i-1][r])+a[i][l]+a[i][r];
for(int i=10;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l+2;r<=n;++r)
chkmx(f[i][l][r],f[i-1][l][r]+a[i][l]+a[i][r]);
Alloc();
for(int i=10;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(f[i-1][l][r],pre[i-1][l][r])+Sum(i,l,r);
Alloc();
for(int i=11;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
f[i][l][r]=max(pre[i-1][l][r],f[i-1][l][r])+a[i][l]+a[i][r];
Reset(g[2]);
for(int i=11;i<=m;++i)
for(int l=1;l<=n;++l)
for(int r=l;r<=n;++r)
chkmx(g[2][i],f[i][l][r]);
}
int P()
{
int Ans=-INF;
for(int i=11;i<=m;++i)
chkmx(Ans,g[2][i]);
return Ans;
}
int main()
{
get(n),get(m);
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
get(a[j][n-i+1]);
for(int j=1;j<=m;++j)
partial_sum(a[j]+1,a[j]+1+n,pref[j]+1);
N(),O(),I();
int Ans=P();
put(Ans),putchar('\n');
return 0;
}

uoj 122 树的计数

发表于 2017-03-01 | 更新于 2018-06-18

Link

给定 序和 序,求同时满足的期望树高 首先重标号,把 序变成bfs[i]==ibfs[i]==ibfs[i]==i的形式 序上的一个区间对应着树的一层,相当于在 序上切几下,构成满足 序的树

Solution 形象的复杂做法

可以发现, 序上相邻的两个点的关系是不会影响其它点之间的关系的,所以就只需要看每对相邻点a,ba,ba,b对答案的贡献即可。记点ppp在 序中的对应位置为ref[p]ref[p]ref[p] aaa和bbb要么是同一层,要么aaa在bbb的上一层 如果aaa在bbb的上一层则有两种情况 否则也有两种情况 讨论

  1. ref[a]>ref[b]ref[a]>ref[b]ref[a]>ref[b],则dep[a]+1==dep[b]dep[a]+1==dep[b]dep[a]+1==dep[b] 如果aaa,bbb在同一层,那么一定有ref[a]<ref[b]ref[a]< ref[b]ref[a]<ref[b]
  2. ref[a]<ref[b]ref[a]< ref[b]ref[a]<ref[b] 1.ref[a]+1==ref[b]ref[a]+1==ref[b]ref[a]+1==ref[b] 可以看出,两种深度关系都存在符合的情况,那就只看会对答案产生影响的dep[a]+1==dep[b]dep[a]+1==dep[b]dep[a]+1==dep[b]的情况 首先排除掉Case #2,那么只需要探究Case #1的成立条件即可 还是把Case #1画得更具体一些吧 aaa是上层的最后一个点,bbb是下层的第一个点,且bbb是aaa的子节点 那么ref[a]ref[a]ref[a]之前的点的深度都≤dep[a]\le dep[a]≤dep[a],ref[b]ref[b]ref[b]之后的所有比bbb深的点与bbb的LCA必然是aaa,即对于bbb与∀p,dep[p]≥dep[b]\forall p,dep[p]\ge dep[b]∀p,dep[p]≥dep[b],ref[b],ref[p]ref[b],ref[p]ref[b],ref[p]之间没有和aaa深度相同的点。 当然,这两个条件对于depdepdep相等的Case #1同时适用,所以对答案的贡献是0.50.50.5。 2.ref[a]+1<ref[b]ref[a]+1< ref[b]ref[a]+1<ref[b],则dep[a]==dep[b]dep[a]==dep[b]dep[a]==dep[b] 假设dep[a]+1==dep[b]dep[a]+1==dep[b]dep[a]+1==dep[b],只能是Case #1。而Case #1中a,ba,ba,b在 序中也一定相邻,所以不符合

Code 第一种实现

最值使用ST表查询,用二分查找来寻找最后一个比bbb大的数字,复杂度O(nlogn)O(n\log n)O(nlogn)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define putd(x) printf("%.3lf",x)
typedef long long int64;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
const int MAXN=2e5+11,LOG=20,INF=0x1f1f1f1f;
using std::max;
using std::min;
namespace _SparseTable_
{
struct Node
{
int mx,mn;
Node(){mx=-INF,mn=INF;}Node(int mx,int mn):mx(mx),mn(mn){}
Node &operator =(int x){mx=mn=x;return *this;}
}f[MAXN][LOG];
Node operator +(Node a,Node b){return Node(max(a.mx,b.mx),min(a.mn,b.mn));}
int log_2[MAXN];
void ST(int a[],int n)
{
log_2[0]=-1;
for(int i=1;i<=n;++i)
{
f[i][0]=a[i];
log_2[i]=log_2[i>>1]+1;
}
for(int j=1;j<=log_2[n];++j)
for(int i=1;i+(1<<j)-1<=n;++i)
f[i][j]=f[i][j-1]+f[i+(1<<j>>1)][j-1];
}
Node RMQ(int l,int r)
{
if(l>r) return Node();
int lo=log_2[r-l+1];
return f[l][lo]+f[r-(1<<lo)+1][lo];
}
int Find(int x,int l,int r)
{
int L=l-1,R=r;
while(L!=R)
{
int Mid=(L+R+1)>>1;
if(RMQ(Mid,r).mx>x) L=Mid;
else R=Mid-1;
}
return L==l-1?-1:L;
}
}using namespace _SparseTable_;
int main()
{
static int ref[MAXN],dfs[MAXN],bfs[MAXN];
int n;get(n);
for(int i=1;i<=n;++i) get(dfs[i]);
for(int i=1;i<=n;++i) get(bfs[i]);
double Ans;
if(n==1)
Ans=1;
else
{
Ans=2;
for(int i=1;i<=n;++i) ref[bfs[i]]=i;
for(int i=1;i<=n;++i) dfs[i]=ref[dfs[i]],bfs[i]=i;
for(int i=1;i<=n;++i) ref[dfs[i]]=i;
ST(dfs,n);
for(int i=2;i<n;++i)
{
int a=i,b=i+1;
if(ref[a]>ref[b])
Ans+=1;
else if(ref[a]+1==ref[b] && RMQ(1,ref[a]).mx<=a && RMQ(ref[b],Find(b,ref[b],n)).mn>a)
Ans+=0.5;
}
}
//putd(Ans-0.001),putchar('\n');
putd(Ans),putchar('\n');
//putd(Ans+0.001),putchar('\n');
return 0;
}

Code 第二种实现

aaa是上层的最后一个点,bbb是下层的第一个点,且bbb是aaa的子节点

条件的另一种判断方法: 序中bbb之后的所有点,在 序中组成恰好连续的一段,那么也完全可以不受bbb的影响。 首先这个结论是正确的,然后假设存在不连续的一段且可以不受bbb的影响,则有 看了一节课不明白 以后再研究吧。 见黄学长的提交here

Solution 抽象的简单做法

根据一些类似的分析可以得到下面三个约束条件

  1. 节点111单独一层
  2. 对于连续的一层,在 序列中对应一段,在 序列中ref[l]<ref[l+1]<...<ref[r]ref[l]< ref[l+1]< ...< ref[r]ref[l]<ref[l+1]<...<ref[r]
  3. dep[dfs[i+1]]≤dep[dfs[i]]+1dep[dfs[i+1]]\le dep[dfs[i]]+1dep[dfs[i+1]]≤dep[dfs[i]]+1

把dep[]dep[]dep[]差分一下得到数组d[]d[]d[],可以把以上三个条件重新写出来

  1. d[1]==1,d[2]==1d[1]==1,d[2]==1d[1]==1,d[2]==1
  2. ref[i]>ref[i+1]ref[i]> ref[i+1]ref[i]>ref[i+1],则d[i+1]==1d[i+1]==1d[i+1]==1
  3. dfs[i+1]>dfs[i]dfs[i+1]> dfs[i]dfs[i+1]>dfs[i],则∑j=dfs[i]+1dfs[i+1]d[j]≤1\sum\limits_{j=dfs[i]+1}^{dfs[i+1]}d[j]\le 1​j=dfs[i]+1​∑​dfs[i+1]​​d[j]≤1

前两个很好处理,现在看第三个 如果取值为000,那么dfs[i]+1...dfs[i+1]dfs[i]+1...dfs[i+1]dfs[i]+1...dfs[i+1]都是一层的。这个条件不需要任何前提就可以满足,所以不用管。 如果取值为111,那就看这些变量里有没有被限制为111的,如果有,其它的只能为000,否则,其它的都是0.50.50.5 被限制为111的只会有一个,因为dfs[i]dfs[i]dfs[i]和dfs[i+1]dfs[i+1]dfs[i+1]的深度相差最多为111,中间一段连续的 序的深度变化也至多为111。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define putd(x) printf("%.3lf",x)
typedef long long int64;
using std::partial_sum;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
const int MAXN=2e5+11;
const double eps=1e-7;
inline int fcmp(const double &x){return -eps<x && x<eps?0:(x<0?-1:1);}
int main()
{
freopen("input","r",stdin);
static int ref[MAXN],dfs[MAXN],bfs[MAXN],limit[MAXN],tag[MAXN];
static double d[MAXN];
int n;get(n);
for(int i=1;i<=n;++i) get(dfs[i]);
for(int i=1;i<=n;++i) get(bfs[i]);
for(int i=1;i<=n;++i) ref[bfs[i]]=i;
for(int i=1;i<=n;++i) dfs[i]=ref[dfs[i]],bfs[i]=i;
for(int i=1;i<=n;++i) ref[dfs[i]]=i;
d[1]=d[2]=1;
for(int i=3;i<=n;++i)
if(ref[i-1]>ref[i])
d[i]=1;
partial_sum(d+1,d+1+n,limit+1);
for(int i=2;i<=n;++i)
if(dfs[i-1]<dfs[i] && limit[dfs[i]]-limit[dfs[i-1]])
tag[dfs[i-1]+1]++,tag[dfs[i]+1]--;
partial_sum(tag+1,tag+1+n,tag+1);
for(int i=1;i<=n;++i)
if(!fcmp(d[i]) && !tag[i])
d[i]=0.5;
partial_sum(d+1,d+1+n,d+1);
//putd(d[n]-0.001),putchar('\n');
putd(d[n]),putchar('\n');
//putd(d[n]+0.001),putchar('\n');
return 0;
}

BZOJ 4515 游戏

发表于 2017-02-28 | 更新于 2018-06-18

Link

去年在考场上精心写了一个暴力,得了5分 究其原因,是因为:

  1. 题读反了
  2. 函数的参数列表里应该写ll写了int

当时还根本没听说过超哥线段树这种技巧

Solution

树链剖分之后,一条链上的点到根节点距离的前缀和len[]len[]len[]是递增的,就可以看做是xxx坐标,可以上超哥线段树了。 对于a×dist(s,r)+ba\times dist(s,r)+ba×dist(s,r)+b这个操作,把链拆成两半 对于sss到lcalcalca这一段,其增加的值为a×(len[s]−len[lca])+b=−a×len[lca]+a×len[s]+ba\times (len[s]-len[lca])+b=-a\times len[lca]+a\times len[s]+ba×(len[s]−len[lca])+b=−a×len[lca]+a×len[s]+b,相当于在线段树上添加一条直线 对于另一段,增加的值为a×(len[r]−len[lca]+len[s]−len[lca])+b=a×len[r]−2a×len[lca]+ba\times (len[r]-len[lca]+len[s]-len[lca])+b=a\times len[r]-2a\times len[lca]+ba×(len[r]−len[lca]+len[s]−len[lca])+b=a×len[r]−2a×len[lca]+b,相当于在线段树上添加一条直线 对于区间查询[l,r][l,r][l,r],对于查询时途径的节点都算一下直线在l,rl,rl,r处的取值,用来更新答案。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define get64(x) scanf("%lld",&x)
#define put64(x) printf("%lld",x)
typedef long long int64;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
const int64 INF=123456789123456789LL;
const int MAXN=1e5+11;
using std::swap;
using std::min;

int64 v[MAXN];
struct Edge
{
Edge *pre;int to,v;
Edge(Edge *pre,int to,int v):pre(pre),to(to),v(v){}
}*id[MAXN];
void Adde(int f,int t,int v)
{
static Edge *Me=(Edge*)malloc((MAXN<<1)*sizeof(Edge));
id[f]=new(Me++) Edge(id[f],t,v);
id[t]=new(Me++) Edge(id[t],f,v);
}
struct Line
{
int64 k,b;
Line(){k=0,b=INF;}Line(int64 k,int64 b):k(k),b(b){}
int64 operator ()(int64 x){return k*x+b;}
};
namespace _SegTree_
{
const int SIZE=MAXN<<2;
struct Node
{
Node *son[2];
int64 minv;
Line ln;
Node(){minv=INF;son[0]=son[1]=0;}
void Up(){chkmn(minv,min(son[0]->minv,son[1]->minv));}
void *operator new(size_t);
}*Pool=(Node*)malloc(SIZE*sizeof(Node)),*Me=Pool,*null=new Node;
void *Node::operator new(size_t){return Me++;}

struct SegTree
{
int L,R;Node *root;
SegTree(){}SegTree(int,int);
void Build(Node*&,int L,int R);
void Append(Node*,int L,int R,Line ln);
void Insert(Node*,int,int,int,int,Line);
void Insert(int l,int r,Line ln){Insert(root,L,R,l,r,ln);}
int64 Access(Node*,int,int,int,int);
int64 Access(int l,int r){return Access(root,L,R,l,r);}
};
SegTree::SegTree(int L,int R):L(L),R(R){Build(root,L,R);}
void SegTree::Build(Node *&pos,int L,int R)
{
pos=new Node;
if(L!=R)
{
int Mid=(L+R)>>1;
Build(pos->son[0],L,Mid);
Build(pos->son[1],Mid+1,R);
}
}
void SegTree::Append(Node *pos,int L,int R,Line ln)
{
if(!pos) return;
chkmn(pos->minv,min(ln(v[L]),ln(v[R])));
Line hl=ln,hr=pos->ln;
if(hl(v[L])>hr(v[L])) swap(hl,hr);
int Mid=(L+R)>>1;double x=-(1.0*hr.b-hl.b)/(1.0*hr.k-hl.k);
if(!(v[L]<=x && x<=v[R])) {pos->ln=hl;return;}
if(x<=v[Mid]) Append(pos->son[0],L,Mid,hl),pos->ln=hr;
else Append(pos->son[1],Mid+1,R,hr),pos->ln=hl;
}
void SegTree::Insert(Node *pos,int L,int R,int l,int r,Line ln)
{
if(L==l && R==r) Append(pos,L,R,ln);
else
{
int Mid=(L+R)>>1;
if(r<=Mid) Insert(pos->son[0],L,Mid,l,r,ln);
else if(Mid+1<=l) Insert(pos->son[1],Mid+1,R,l,r,ln);
else Insert(pos->son[0],L,Mid,l,Mid,ln),Insert(pos->son[1],Mid+1,R,Mid+1,r,ln);
pos->Up();
}
}
int64 SegTree::Access(Node *pos,int L,int R,int l,int r)
{
if(L==l && R==r) return pos->minv;
else
{
int Mid=(L+R)>>1;int64 res=min(pos->ln(v[l]),pos->ln(v[r]));
if(r<=Mid) chkmn(res,Access(pos->son[0],L,Mid,l,r));
else if(Mid+1<=l) chkmn(res,Access(pos->son[1],Mid+1,R,l,r));
else chkmn(res,min(Access(pos->son[0],L,Mid,l,Mid),Access(pos->son[1],Mid+1,R,Mid+1,r)));
return res;
}
}
}using _SegTree_::SegTree;
namespace _Divide_
{
struct Node{int top;}*Me=(Node*)malloc(MAXN*sizeof(Node)),*cn[MAXN];
int fa[MAXN],dep[MAXN],sz[MAXN],prefer[MAXN],dfs[MAXN],lbd[MAXN],rbd[MAXN],dc;
int64 len[MAXN];
SegTree seg;
void DFS(int pos)
{
sz[pos]=1;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(u==fa[pos]) continue;
fa[u]=pos;
dep[u]=dep[pos]+1;
len[u]=len[pos]+e->v;
DFS(u);
if(sz[u]>sz[prefer[pos]]) prefer[pos]=u;
sz[pos]+=sz[u];
}
}
void Assign(int pos)
{
dfs[++dc]=pos;
lbd[pos]=dc;
if(prefer[pos])
{
Assign(prefer[pos]),cn[pos]=cn[prefer[pos]];
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(u==fa[pos] || u==prefer[pos]) continue;
Assign(u),cn[u]->top=u;
}
}
else
cn[pos]=new(Me++) Node;
rbd[pos]=dc;
}
void Divide()
{
DFS(1);
Assign(1),cn[1]->top=1;
seg=SegTree(1,dc);
for(int i=1;i<=dc;++i) v[i]=len[dfs[i]];
}
int LCA(int p1,int p2)
{
while(cn[p1]!=cn[p2])
dep[cn[p1]->top]>dep[cn[p2]->top]?p1=fa[cn[p1]->top]:p2=fa[cn[p2]->top];
return dep[p1]<dep[p2]?p1:p2;
}
void Modify(int bot,int top,Line ln)
{
while(cn[bot]!=cn[top])
{
seg.Insert(lbd[cn[bot]->top],lbd[bot],ln);
bot=fa[cn[bot]->top];
}
seg.Insert(lbd[top],lbd[bot],ln);//写反了??
}
void Modify(int s,int t,int64 a,int64 b)
{
int lca=LCA(s,t);
Modify(s,lca,Line(-a,a*len[s]+b));
Modify(t,lca,Line(a,a*(-2*len[lca]+len[s])+b));
}
int64 Query(int p1,int p2)
{
int64 res=INF;
while(cn[p1]!=cn[p2])
{
if(dep[cn[p1]->top]<dep[cn[p2]->top]) swap(p1,p2);
chkmn(res,seg.Access(lbd[cn[p1]->top],lbd[p1]));
p1=fa[cn[p1]->top];
}
if(lbd[p1]>lbd[p2]) swap(p1,p2);
chkmn(res,seg.Access(lbd[p1],lbd[p2]));
return res;
}
}using namespace _Divide_;
int main()
{
int n,m;get(n),get(m);
for(int i=1;i<=n-1;++i)
{
int u,v,w;get(u),get(v),get(w);
Adde(u,v,w);
}
Divide();
for(int i=1;i<=m;++i)
{
int opt,s,t;
int64 a,b;
get(opt),get(s),get(t);
if(opt==1)
{
get64(a),get64(b);
Modify(s,t,a,b);
}
else
{
int64 Ans=Query(s,t);
put64(Ans),putchar('\n');
}
}
return 0;
}

uoj7 购票

发表于 2017-02-27 | 更新于 2018-06-18

Link

一道题,说明我斜率优化和CDQ分治都学的一知半解。所以会写的很详细。

Solution

30pts

f[i]=min{f[j]+dist(i,j)∗p[i]+q[i],dist(i,j)≤limt[i]}f[i]=min\{f[j]+dist(i,j)*p[i]+q[i],dist(i,j)\le limt[i]\}f[i]=min{f[j]+dist(i,j)∗p[i]+q[i],dist(i,j)≤limt[i]} 用来对拍

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
namespace KISS
{
void DP(int pos)
{
f[pos]=LLONG_MAX;
int par=c[pos].fa;
ll d=c[pos].s;
while(par && d<=c[pos].lim)
{
if(chkmn(f[pos],f[par]+d*c[pos].k))
pre[pos]=par;
d+=c[par].s;
par=c[par].fa;
}
if(pos!=1) f[pos]+=c[pos].b;
else f[pos]=0;
for(Edge *e=id[pos];e;e=e->pre)
DP(e->to);
}
void Solve(){DP(1);}
}

40pts

考虑在序列上,无限制的情况。 f[i]=min{f[j]+dist(i,j)×p[i]+q[i]}=q[i]+min{f[j]+dist(i,j)×p[i]}f[i]=min\{f[j]+dist(i,j)\times p[i]+q[i]\}=q[i]+min\{f[j]+dist(i,j)\times p[i]\}f[i]=min{f[j]+dist(i,j)×p[i]+q[i]}=q[i]+min{f[j]+dist(i,j)×p[i]} 看起来很斜率优化。 设j<kj< kj<k,kkk优于jjj当且仅当 f[j]+dist(i,j)×p[i]>f[k]+dist(i,k)×p[i]f[j]+dist(i,j)\times p[i]> f[k]+dist(i,k)\times p[i]f[j]+dist(i,j)×p[i]>f[k]+dist(i,k)×p[i] 即 f[j]−len[j]×p[i]>f[k]−len[k]×p[i]f[j]-len[j]\times p[i]> f[k]-len[k]\times p[i]f[j]−len[j]×p[i]>f[k]−len[k]×p[i] f[j]−f[k]>(len[j]−len[k])×p[i]f[j]-f[k]> (len[j]-len[k])\times p[i]f[j]−f[k]>(len[j]−len[k])×p[i] 给定s>0s>0s>0,所以len[]len[]len[]递增,可以化为 f[j]−f[k]len[j]−len[k]<p[i]\dfrac {f[j]-f[k]}{len[j]-len[k]}< p[i]​len[j]−len[k]​​f[j]−f[k]​​<p[i] 维护一个下凸壳。 p[]p[]p[]并不是递增的,所以不能用单调队列,需要用单调栈+二分。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
namespace NakedOptOnChain
{
struct Node
{
ld slop;int id;
Node(){}
Node(ld slop,int id):slop(slop),id(id){}
bool operator <(const Node &a)const{return fcmp(slop-a.slop)<0;}

}Q[MAXN];int he,ta;
void Solve()
{
//x坐标单调,k不单调,维护斜率下凸壳,二分查找
static ll len[MAXN];
for(int i=2;i<=n;++i) len[i]=len[i-1]+c[i].s;
new(&Q[he=ta=1])Node(inf,1);
#define transfer(j,i) (f[j]+(len[i]-len[j])*c[i].k+c[i].b)
#define slope(y,x) ((ld)(y)/(x))
#define count(x,y) (y-x+1)
for(int i=2;i<=n;++i)
{
int index=lower_bound(Q+he,Q+ta,Node((ld)c[i].k,0))-Q;
int j=Q[index].id;
f[i]=min(j!=0?transfer(j,i):LLONG_MAX,transfer(Q[ta].id,i));//j==0...
while(count(he,ta)>=2
&& fcmp(Q[ta-1].slop-slope(f[i]-f[Q[ta].id],len[i]-len[Q[ta].id]))>=0)
ta--;
Q[ta].slop=slope(f[i]-f[Q[ta].id],len[i]-len[Q[ta].id]);
Q[++ta]=Node(inf,i);
}
#undef transfer
#undef slope
#undef count
}
}

50pts

加上了路径限制。一开始的想法显然就是在单调栈中二分到第一个可以取到的位置,然后再二分查找最优位置,当时觉得应该不行,但也没想出反例。写出来之后WA,发现反例如下 所以这样是不行的。。 而CDQ分治恰好可以对区间的两半分别做一些操作而不影响答案,于是就CDQ分治 第一维,按照可以到最靠左的点排序;第二维,分治区间 在统计答案的时候,两个指针都从右向左走,维护满足距离限制的凸壳 回溯的时候按照标号的大小顺序归并

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
namespace DCOnChain
{
ll len[MAXN];
struct Opt
{
int id;ll fr;
bool operator <(const Opt &a) const{return fr<a.fr;}
}p[MAXN],t[MAXN];
bool CmpID(const Opt &a,const Opt &b){return a.id<b.id;}
int Partition(int L,int R)
{
int Mid=(L+R)>>1,pl=L,pr=Mid+1;
for(int i=L;i<=R;++i) t[p[i].id<=Mid?pl++:pr++]=p[i];
copy(t+L,t+R+1,p+L);
return Mid;
}
void Merge(int L,int Mid,int R)
{
merge(p+L,p+Mid+1,p+Mid+1,p+R+1,t+L,CmpID);
copy(t+L,t+R+1,p+L);
}
struct Point
{
ll x,y;int id;
Point(){}
Point(int id):id(id)
{
x=len[id];
y=f[id];
}
Point(ll x,ll y):x(x),y(y){}
Point operator -(const Point &a)const{return Point(x-a.x,y-a.y);}
}stack[MAXN];int top;
ld Outer(Point a,Point b){return (ld)a.x*b.y-(ld)a.y*b.x;}//叉积爆ll没加强转ld..........
ld Slope(Point a,Point b){return ((ld)a.y-b.y)/(a.x-b.x);}
int BinarySearch(ll k)
{
if(!top) return 0;
int L=1,R=top;
while(L!=R)
{
int Mid=(L+R+1)>>1;
if(Mid==1 || fcmp(Slope(stack[Mid],stack[Mid-1])-k)>=0) L=Mid;
//stack[top]???....
else R=Mid-1;
}
return stack[L].id;
}
ll transfer(int j,int i) {return (f[j]+(len[i]-len[j])*c[i].k+c[i].b);}
void DC(int L,int R)
{
if(L==R) return;
int Mid=Partition(L,R);
DC(L,Mid);
top=0;
for(int pl=Mid,pr=R;pr>Mid;--pr)
{
int goal=p[pr].id;
while(pl>=L && len[p[pl].id]>=p[pr].fr)
{
Point cur(p[pl].id);
while(top>=2 && fcmp(Outer(stack[top]-stack[top-1],cur-stack[top]))>=0)//叉的顺序写反了
top--;
stack[++top]=cur;
pl--;
}
int best=BinarySearch(c[goal].k);
if(best && chkmn(f[goal],transfer(best,goal))) pre[goal]=best;
}
DC(Mid+1,R);
Merge(L,Mid,R);
}
void Solve()
{
for(int i=1;i<=n;++i)
{
len[i]=len[i-1]+c[i].s;
p[i].id=i;p[i].fr=len[i]-c[i].lim;
f[i]=LLONG_MAX;
}
f[1]=0;
sort(p+1,p+1+n);
DC(1,n);
}
}

100pts

这一步做法就非常妙了。 在树上要满足距离限制,也可以借鉴CDQ分治的思路,把用来更新的点和被更新的点隔离开,就可以分别进行一些操作 对树点分治,每层找重心,找到重心之后先把重心的父节点对应的子树DC,然后用从重心的父节点开始,沿着父指针一直走到当前层之外,用这条链上满足距离限制的点更新重心的答案,然后把重心也加进这条链。取出剩下的子树中的所有节点,再按照序列上的做法用那条链更新剩下的节点。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
namespace DCOnTree
{
ll len[MAXN];
bool ud[MAXN]={1};
int sz[MAXN],dep[MAXN];
struct Opt
{
int id;ll fr;
Opt(){}Opt(int id,ll fr):id(id),fr(fr){}
bool operator <(const Opt &a) const{return fr<a.fr;}
}p[MAXN],t[MAXN];
bool CmpID(const Opt &a,const Opt &b){return a.id<b.id;}

struct Point
{
ll x,y;int id;
Point(){}
Point(int id):id(id){x=len[id];y=f[id];}
Point(ll x,ll y):x(x),y(y){}
Point operator -(const Point &a)const{return Point(x-a.x,y-a.y);}
}stack[MAXN];int top;
ld Outer(Point a,Point b){return (ld)a.x*b.y-(ld)a.y*b.x;}//叉积爆ll没加强转ld..........
ld Slope(Point a,Point b){return ((ld)a.y-b.y)/(a.x-b.x);}
void CalSize(int pos,int from)
{
sz[pos]=1;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
CalSize(u,pos);
sz[pos]+=sz[u];
}
}
int DP(int pos,int from,int tol)
{
static int f[MAXN]={INT_MAX};
int res=0,temp;f[pos]=0;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
temp=DP(u,pos,tol);
if(res==0 || f[res]>f[temp]) res=temp;
chkmx(f[pos],sz[u]);
}
chkmx(f[pos],tol-sz[pos]-1);
return f[res]<f[pos]?res:pos;
}
int Center(int rt)
{
CalSize(rt,0);
return DP(rt,0,sz[rt]);
}
void Others(int pos,int from,Opt *p,int &pc)
{
if(!ud[pos]) p[++pc]=Opt(pos,len[pos]-c[pos].lim);
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
Others(u,pos,p,pc);
}
}
int BinarySearch(ll k)
{
if(!top) return 0;
int L=1,R=top;
while(L!=R)
{
int Mid=(L+R+1)>>1;
if(Mid==1 || fcmp(Slope(stack[Mid],stack[Mid-1])-k)>=0) L=Mid;
//stack[top]???....
else R=Mid-1;
}
return stack[L].id;
}
ll transfer(int j,int i) {return (f[j]+(len[i]-len[j])*c[i].k+c[i].b);}
void DC(int pos)
{
if(!pos || ud[pos]) return;
int gc=Center(pos);
int above=gc;
while(!ud[above]) above=c[above].fa;
ud[gc]=1;
DC(c[gc].fa);//
int pc=0,tc;//front..
for(int cur=gc;cur!=above;cur=c[cur].fa)
{
p[++pc]=Opt(cur,len[cur]-c[cur].lim);
if(cur!=gc && len[cur]>=p[1].fr && chkmn(f[gc],transfer(cur,gc))) pre[gc]=cur;
}
reverse(p+1,p+1+pc);

Others(gc,0,t,tc=0);sort(t+1,t+1+tc);
for(top=0;tc;tc--)
{
int goal=t[tc].id;
while(pc && len[p[pc].id]>=t[tc].fr)
{
Point cur(p[pc--].id);
while(top>=2 && fcmp(Outer(stack[top]-stack[top-1],cur-stack[top]))>=0)
//if..........!!
top--;
stack[++top]=cur;
}
int best=BinarySearch(c[goal].k);
if(best && chkmn(f[goal],transfer(best,goal))) pre[goal]=best;
}
for(Edge *e=id[gc];e;e=e->pre)
{
int u=e->to;
if(!ud[u]) DC(u);
}
}
void Prep()
{
memset(f,127,sizeof(f));f[1]=0;
static int stack[MAXN],top;
stack[top=1]=1;dep[1]=1;
while(top)
{
int cur=stack[top--];
for(Edge *e=id[cur];e;e=e->pre)
{
int u=e->to;
if(dep[u]) continue;
stack[++top]=u;
dep[u]=dep[cur]+1;
len[u]=len[cur]+c[u].s;
}
}
}
void Solve()
{
Prep();
DC(1);
}
}

Solution

这是第二次做到考算法新推广的题 第一次是用替罪羊思想维护动态点分治 做不出来甚至看不懂题解说明这种算法本身就学的不扎实 以后再碰到这种情况也应该像这样写出退化版问题的程序,可以加深对原算法和推广的算法的理解

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define get64(x) scanf("%lld",&x)
#define put64(x) printf("%lld",x)
typedef long long ll;
typedef long double ld;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::lower_bound;
using std::min;
using std::copy;
using std::sort;
using std::merge;
using std::greater;
using std::reverse;

const int MAXN=2e5+11;
const ld eps=1e-5,inf=1e100;
inline int fcmp(ld x){return -eps<x && x<eps?0:(x<0?-1:1);}
struct City
{
int fa;ll s,k,b,lim;
}c[MAXN];int n;
ll f[MAXN];
struct Edge
{
Edge *pre;int to;
Edge(Edge *pre,int to):pre(pre),to(to){}
void *operator new(size_t);
}*Edge_Me=(Edge*)malloc((MAXN<<1)*sizeof(Edge)),*id[MAXN];
void *Edge::operator new(size_t){return Edge_Me++;}
void Adde(int x,int y){id[x]=new Edge(id[x],y),id[y]=new Edge(id[y],x);}

ll len[MAXN];
bool ud[MAXN]={1};
int sz[MAXN];
struct Opt
{
int id;ll fr;
Opt(){}Opt(int id,ll fr):id(id),fr(fr){}
bool operator <(const Opt &a) const{return fr<a.fr;}
}p[MAXN],t[MAXN];
bool CmpID(const Opt &a,const Opt &b){return a.id<b.id;}

struct Point
{
ll x,y;int id;
Point(){}
Point(int id):id(id){x=len[id];y=f[id];}
Point(ll x,ll y):x(x),y(y){}
Point operator -(const Point &a)const{return Point(x-a.x,y-a.y);}
}stack[MAXN];int top;
ld Outer(Point a,Point b){return (ld)a.x*b.y-(ld)a.y*b.x;}//叉积爆ll没加强转ld..........
ld Slope(Point a,Point b){return ((ld)a.y-b.y)/(a.x-b.x);}
void CalSize(int pos,int from)
{
sz[pos]=1;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
CalSize(u,pos);
sz[pos]+=sz[u];
}
}
int DP(int pos,int from,int tol)
{
static int f[MAXN]={INT_MAX};
int res=0,temp;f[pos]=0;
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
temp=DP(u,pos,tol);
if(res==0 || f[res]>f[temp]) res=temp;
chkmx(f[pos],sz[u]);
}
chkmx(f[pos],tol-sz[pos]-1);
return f[res]<f[pos]?res:pos;
}
int Center(int rt)
{
CalSize(rt,0);
return DP(rt,0,sz[rt]);
}
void Others(int pos,int from,Opt *p,int &pc)
{
if(!ud[pos]) p[++pc]=Opt(pos,len[pos]-c[pos].lim);
for(Edge *e=id[pos];e;e=e->pre)
{
int u=e->to;
if(ud[u] || u==from) continue;
Others(u,pos,p,pc);
}
}
int BinarySearch(ll k)
{
if(!top) return 0;
int L=1,R=top;
while(L!=R)
{
int Mid=(L+R+1)>>1;
if(Mid==1 || fcmp(Slope(stack[Mid],stack[Mid-1])-k)>=0) L=Mid;
//stack[top]???....
else R=Mid-1;
}
return stack[L].id;
}
ll transfer(int j,int i) {return (f[j]+(len[i]-len[j])*c[i].k+c[i].b);}
void DC(int pos)
{
if(!pos || ud[pos]) return;
int gc=Center(pos);
int above=gc;
while(!ud[above]) above=c[above].fa;
ud[gc]=1;
DC(c[gc].fa);//
int pc=0,tc;//front..
for(int cur=gc;cur!=above;cur=c[cur].fa)
{
p[++pc]=Opt(cur,len[cur]-c[cur].lim);
if(cur!=gc && len[cur]>=p[1].fr) chkmn(f[gc],transfer(cur,gc));
}
reverse(p+1,p+1+pc);

Others(gc,0,t,tc=0);sort(t+1,t+1+tc);
for(top=0;tc;tc--)
{
int goal=t[tc].id;
while(pc && len[p[pc].id]>=t[tc].fr)
{
Point cur(p[pc--].id);
while(top>=2 && fcmp(Outer(stack[top]-stack[top-1],cur-stack[top]))>=0)
top--;
stack[++top]=cur;
}
int best=BinarySearch(c[goal].k);
if(best) chkmn(f[goal],transfer(best,goal));
}
for(Edge *e=id[gc];e;e=e->pre)
{
int u=e->to;
if(!ud[u]) DC(u);
}
}
void Prep()
{
memset(f,127,sizeof(f));f[1]=0;
static int stack[MAXN],top;
stack[top=1]=1;
while(top)
{
int cur=stack[top--];
for(Edge *e=id[cur];e;e=e->pre)
{
int u=e->to;
if(len[u] || u==1) continue;
stack[++top]=u;
len[u]=len[cur]+c[u].s;
}
}
}
int main()
{
int t;get(n),get(t);
for(int i=2;i<=n;++i)
{
get(c[i].fa);
get64(c[i].s);
get64(c[i].k),get64(c[i].b);
get64(c[i].lim);
Adde(c[i].fa,i);
}
Prep();
DC(1);
for(int i=2;i<=n;++i)
put64(f[i]),putchar('\n');
return 0;
}

BZOJ 1568 BlueMary开公司

发表于 2017-02-27 | 更新于 2018-06-18

Link

Solution

超神线段树,又叫超哥线段树,又叫李超线段树 在线段树的节点上存储直线,当添加新的直线的时候按照交点讨论选择将一条线压到子区间 首先,如果一条直线被另一条完全覆盖,可以直接把被覆盖的扔掉(但其实不需要单独讨论) 然后对于两条直线,设在点LLL处取值较大的直线为l1l_1l​1​​,另一条为l2l_2l​2​​。 如果两线段的交点在区间中点左侧,把l1l_1l​1​​压到左半区间 否则将l2l_2l​2​​压到右半区间 查询的时候,在覆盖该点的所有线段树节点存储的直线中取一个最大值即可。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//Code by Lucida
#include<bits/stdtr1c++.h>
#define get(x) scanf("%d",&x)
#define put(x) printf("%d",x)
#define gets(x) scanf("%s",x)
#define getd(x) scanf("%lf",&x)
#define put64(x) printf("%lld",x)
typedef long long ll;
template <class T> inline bool chkmx(T &a,const T &b){return a<b?a=b,1:0;}
template <class T> inline bool chkmn(T &a,const T &b){return a>b?a=b,1:0;}
using std::max;
using std::swap;

const int MAXN=1e5+11,DAY=50000;

struct Line
{
double k,b;
Line(){k=b=0;}
Line(double k,double b):k(k),b(b){}
double operator ()(int x){return k*x+b;}
};

namespace _ChaoSeg_
{
const int SIZE=MAXN<<2;

struct Node
{
Node *son[2];
Line line;
Node(){son[0]=son[1]=0;}
void *operator new(size_t);
}*Me=(Node*)malloc(SIZE*sizeof(Node));
void *Node::operator new(size_t){return Me++;}
struct ChaoSeg
{
int L,R;Node *root;
ChaoSeg(int,int);
void Build(Node*&,int,int);
void Append(Node*,Line,int,int);
void Insert(Node*,int,int,int,int,Line);
double Query(Node*,int,int,int);

void Insert(int l,int r,Line ln){Insert(root,L,R,l,r,ln);}
double Query(int x){return Query(root,L,R,x);}
};
ChaoSeg::ChaoSeg(int L,int R):L(L),R(R){Build(root,L,R);}
void ChaoSeg::Build(Node *&pos,int L,int R)
{
pos=new Node;
if(L!=R)
{
int Mid=(L+R)>>1;
Build(pos->son[0],L,Mid);
Build(pos->son[1],Mid+1,R);
}
}
void ChaoSeg::Insert(Node* pos,int L,int R,int l,int r,Line ln)
{
if(L==l && R==r) Append(pos,ln,L,R);
else
{
int Mid=(L+R)>>1;
if(r<=Mid) Insert(pos->son[0],L,Mid,l,r,ln);
else if(Mid+1<=l) Insert(pos->son[1],Mid+1,R,l,r,ln);
else Insert(pos->son[0],L,Mid,l,Mid,ln),Insert(pos->son[1],Mid+1,R,Mid+1,r,ln);
}
}
void ChaoSeg::Append(Node *pos,Line ln,int L,int R)
{
if(!pos) return;
Line hl=ln,hr=pos->line;
if(hl(L)<hr(L)) swap(hl,hr);
double x=-(hl.b-hr.b)/(hl.k-hr.k);//-..
if(!(L<=x && x<=R)){pos->line=hl;return;}
int Mid=(L+R)>>1;
if(x<=Mid) pos->line=hr,Append(pos->son[0],hl,L,Mid);
else pos->line=hl,Append(pos->son[1],hr,Mid+1,R);
}
double ChaoSeg::Query(Node *pos,int L,int R,int goal)
{
if(L==R) return pos->line(goal);
else
{
int Mid=(L+R)>>1;double res=pos->line(goal);
if(goal<=Mid) return max(res,Query(pos->son[0],L,Mid,goal));
else return max(res,Query(pos->son[1],Mid+1,R,goal));
}
}
}using _ChaoSeg_::ChaoSeg;
int main()
{
int m;get(m);
ChaoSeg seg(1,DAY);
for(int i=1;i<=m;++i)
{
char opt[15];gets(opt);
int day;ll Ans;double k,b;
switch(opt[0])
{
case 'P':
getd(b),getd(k);
seg.Insert(1,DAY,Line(k,b-k));
break;
case 'Q':
get(day);
Ans=(ll)seg.Query(day)/100;
put64(Ans),putchar('\n');
break;
}
}
return 0;
}
1…101112…23
Lucida

Lucida

It's been a while.

222 日志
127 标签
© 2018 Lucida
由 Hexo 强力驱动 v3.7.1
|
主题 — NexT.Muse v6.3.0