联赛模拟12
T1 松鼠的新家
做过的原题,lca+树上差分,但要注意算重的情况,将最终(a[2--n])的结果各减去一
T2 trade
(70)分:
看到题目第一眼想到了(dp),(dp[i][j]),但如果记录前(i)个买/卖了(j)个的最大利润,你会发现无法转移,
可以用(dp[i][j])表示前i个货物处理完之后手中剩下(j)个的最大利润,转移就比较简单,考虑当前的第i个买还是卖还是忽略:
(dp[i][j]=max(dp[i-1][j],max(dp[k][j-1]-a[i],dp[k][j+1]+a[i]));)
时间(n^3)的,然后取个前缀(max)就不用枚举(k)了,并且发现压掉一维(i)也可以,所以时间(n^2),空间(O(n))
一直想再优化,无果,(70)分等死
(100)分:
可反悔贪心,不记得以前做过这么维护的题。
用小根堆维护,当前可以卖的物品。
每扫到一个物品,若它不大于堆顶,入堆。
否则出堆顶,向堆中放入2个该物品,加上贡献(val-top().val)
设当前物品是(b),堆顶是(a),以后的一件物品(c)
第一个该物品是以后进行反悔时,(a)卖为(c)比卖为(b)更优,则会将(b)取出换为(c),
第二个(b)是又可以重新作为商品在堆里被卖;
code
if(q.empty()||x<=q.top().val) q.push((Node){i,x});
else{
ans+=(x-q.top().val);
q.pop();
q.push((Node){i,x});
q.push((Node){i,x});
}
T3 sum
(q)个询问,每个询问给定n,m,求(sum_{i=0}^{m}C_n^i)
重要规律:当(m)一定时,(s[i])表示(n=i)的答案,则(s[i]=s[i-1]+C_{n-1}^m),可以结合杨辉三角来看
显然规律:n一定时,(s[i])表示(m=i)的答案,那么可以O(1)求出(s[i-1],s[i+1]);
用上面两条规律可以发现(s[n][m]),的n和m当作左右端点可以当作莫队的板子题来做(然而我以及好多人用这两条规律水到了80)
(除2要乘逆元我竟然忘了卡了好长时间)
T4
暴力有40
正解:
总楼盘数差分+前缀和
联通块数用(vector)+并查集来做,并查集每次合并的交界只会被访问一次所以复杂度正确。
晚间测试5
容易题
兔子与樱花
发现如果底层节点可以合并,那么一定不会造成总结果变少(主要是考场上没造出反例……)
但是每个节点删儿子时,要从小往大删,x的每个儿子(yin son[x])贡献((son_{cnt}[y]-1)+c[y]),当然y每贡献一次(c[x])也要改
对于每个节点开(vector),对儿子排序,(dfs)向上更新即可
code
struct Node{
int id;
int val;
bool operator < (const Node &B)const{
return val<B.val;
}
};
vector <Node> g[maxn];
void dfs(rint x){
if(!head[x]) return;
for(rint i=head[x],y;i;i=e[i].next){
y=e[i].to;
dfs(y);
g[x].push_back((Node){y,c[y]+son_c[y]-1});
}
sort(g[x].begin(),g[x].end());
for(rint i=0,id,val;i<g[x].size();++i){
id=g[x][i].id,val=g[x][i].val;
if(c[x]+son_c[x]+val<=m){
c[x]+=c[id];
son_c[x]=son_c[x]-1+son_c[id];
ans++;
}
else break;
}
}