这一场打的很烂,队友一直在开计算几何没有做出来,而我也不会做,最近被好几场比赛压着了,所以我还是简短的写一下题吧,补题等到后面再说吧。
1006 Maex(树形DP)
题意:给你一个n个点的树,求每一个结点包括其子树的MEX值的和的最大值。
思路:对于一个子树来看,通过对于MEX的理解,如果没有出现0那么值一直为0,所以我们想要填0。那么填完之后应该填什么?应该填1...我想要求出最大值,那么肯定是想要0在最底下,然后让上面的都可以利用它,这样就是一个最长链了。那么就求深度然后得到最长链填0吗,如果子树的其他支链有什么关系呢?我们来看下面这个图:
这张图可以对比两种:左边是最深链,右边是多链
我们对其分别进行计算,发现并不是最深链,还需要看其他支链。原因是我虽然链少,但是我可以通过其他子结点来堆,使得我的当前结点变高,例如这个四个父亲的结点6,因为子结点很多,它的MEX可以很大。
左边是最深链,右边是多链.很明显,不能只看最深链
所以我们需要维护两个值,一个是子结点个数,这样来看加上子结点个数就是自己的MEX值,然后每次加上儿子结点的Max值即可。
代码:
inline void dfs(int x,int pre)
{
num[x] = 1;//首先肯定得有自己
int res = 0;
for (int i=head[x];i;i=edge[i].next)
{
int y = edge[i].to;
if (y==pre) continue;
dfs(y,x);
res = Max(res, f[y]);
num[x] += num[y];//维护子结点个数
}
f[x] = res + num[x];//维护当前答案
}
inline void Case_Test()
{
cin>>n;
for (int i=1;i<=n;i++) head[i] = 0;
cnt = 0;
for (int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
add(u,v);
add(v,u);
}
dfs(1,0);
cout<<f[1]<<endl;
}
1012 Loop(链表)
题意:给你一个长度为n的序列,你可以进行k次操作,选择一个数让其放到后面的任意位置(可以不动),求操作结束后的最大序列(字典序最大)。
思路:经过推断,可以先求出逆序对,例如[4,3,1,2]那么说明如果有机会我们需要将1放到2的后面使其变成[4,3,2,1]。这样就能保证字典序最大。
那么如果有多个逆序对怎么办呢?例如[4,5,1,2,7,6,5,7],图中有[1,2],[2,7],[5,7]三对逆序对,我们需要交换最大的[5,7]吗?不是,我们需要交换最前面的[1,2],因为字典序是从前往后的,所以前面的满足条件的话当然是最大的。
那么相当于我取出来两个数,接下来如何看?例如上面那个例子,我取出[1]实际上现在应该看[4,5,2,7,6,5,7]这个时候要比较2两端的,所以我们需要用O(1)的链表来做。
inline void remove(int cur)
{
vis[cur] = 0;
r[ l[cur] ]=r[cur];
l[ r[cur] ]=l[cur];
int x = l[cur], y = r[cur];
if (a[x]<a[y]) q.push(x);//又产生逆序对
}
所以我们用一个优先队列来存放每次的最前面的,每次取出来的就是最前面的。
那么取出来的数放在哪呢?
放在最后第一个降序的地方,一次往前面插,对于当前的数,把大于它的插前面,这样保证字典序最大,例如现在变成[3,4,5,7,\underline{3},1],现在取出来了[4,3,2]那么将4放在3前面,3,2放在1前面。
int l[N],r[N];
priority_queue<int,vector<int>,greater<int>> q;
priority_queue<int> num;
inline void remove(int cur)
{
vis[cur] = 0;
r[ l[cur] ]=r[cur];
l[ r[cur] ]=l[cur];
int x = l[cur], y = r[cur];
if (a[x]<a[y]) q.push(x);
}
inline void Case_Test()
{
cin>>n>>k;
vector<int> ans;
while (!q.empty()) q.pop();
for (int i = 1; i <= n ;i++)
{
l[i] = i-1, r[i] = i+1;
cin>>a[i];
vis[i] = 1;
}
a[0] = inf, a[n+1] = -inf;
r[0] = 1, l[n+1] = 1;
l[1] = 0, r[n+1] = 0;
for (int i=1;i<=n;i++)
if (a[i]<a[i+1])
q.push(i);//首先计算逆序对,进队等待操作
while (k--)
{
if (q.empty()) break;
int cur = q.top();q.pop();//取出进行链表操作
remove(cur);
num.push(a[cur]);
}
int pos = n+1, pre = 0;
for (int i=1;i<=n;i++)
{
if (!vis[i]) continue;
if (a[i]<pre) {pos = i;break;}
else
{
ans.push_back(a[i]);
pre = a[i];
}
}
for (int i=pos;i<=n;i++)
{
if (!vis[i]) continue;
while (!num.empty() && num.top()>a[i])
{
ans.push_back(num.top());
num.pop();
}
ans.push_back(a[i]);
}
while (!num.empty())
{
ans.push_back(num.top());
num.pop();
}//还没有插完
for (int i=0;i<ans.size();i++)
cout<<ans[i]<<" \n"[i+1==ans.size()];
}
文章评论