首页 > 其他分享 >ucup nanjing 题解

ucup nanjing 题解

时间:2023-12-03 12:03:41浏览次数:36  
标签:cur min int 题解 差分 dif2 nanjing len ucup

比赛链接

D

收获很大的一道题
首先考虑朴素的 \(dp\),令 \(f_{x,i}\) 为 \(x\) 子树中的每一个叶子到 \(x\) 的距离都为 \(i\) 的最小代价
不难列出 \(dp\) 式子为:\(f_{x,i}=\min\limits_{i\in \{0,1\}}\{cost(u,i)+\sum\limits_{v\in son(u)}f_{v,x-i}\}\),其中 \(cost(u,i)\) 为把 \(u\) 变成颜色 \(0/1\) 的代价( \(black=1,red=0\))

可以发现 \(f_u\) 是下凸的
因为 \(cost_{u,0/1}\) 是下凸的,凸序列按位 \(\sum\) 仍是凸的,凸序列做 \(\min +\) 卷积也是凸的

考虑一个很妙的东西:维护下凸序列的差分序列(把正差分,负差分,\(0\) 差分分开存),是单调不降的
合并时好合并的,直接按位加即可
考虑如何添加进 \(u\) 的贡献
当 \(col_u=1\) 时,\(f_{v,i} \to f_{x,i}\) 需要多 \(1\) 的贡献,在凸壳上分析一下可得,在差分 \(<0\) 时,这样是优的,所以直接在负差分的后面加入 \(-1\) 即可
当 \(col_u=0\) 时,类似分析可得,在正差分前面加入 \(1\) 即可

来分析一下时间复杂度
显然,长度为 \(a,b\) 的凸序列合并,我们只需要保留前 \(\min(a,b)\) 项
考虑删除元素时的贡献,即 \(\max(a,b)\) 个元素会产生 \(a+b\) 的贡献,所以分给每个元素的平均贡献不超过 \(2\)
所以总贡献是 \(O(n)\) 级别的
时间复杂度为 \(O(n)\)

#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
inline int read(){
    int FF=0,RR=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
    for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
    return FF*RR;
}
const int N=100100;
int ans[N];
char str[N];
vector<int> G[N];
struct Node{
    int f0,negtot,s0;
    vector<int> dif1,dif2;//dif1:负差分  dif2:正差分
}f[N];
Node merge(Node x,Node y){
    Node ret;ret.f0=x.f0+y.f0,ret.s0=0,ret.negtot=0;
    int len=min(x.dif1.size()+x.dif2.size()+x.s0,y.dif1.size()+y.dif2.size()+y.s0);
    vector<int> rec(len);
    int cur=0;
    for(int v:x.dif1){ if(cur>=len) break;rec[cur++]+=v;}
    cur=min(len,cur+x.s0);
    reverse(x.dif2.begin(),x.dif2.end());
    for(int v:x.dif2){ if(cur>=len) break;rec[cur++]+=v;}
    cur=0;
    for(int v:y.dif1){ if(cur>=len) break;rec[cur++]+=v;}
    cur=min(len,cur+y.s0);
    reverse(y.dif2.begin(),y.dif2.end());
    for(int v:y.dif2){ if(cur>=len) break;rec[cur++]+=v;}
    for(int v:rec){
        if(v<0) ret.dif1.pb(v),ret.negtot+=v;
        else if(v==0) ret.s0++;
        else ret.dif2.pb(v);
    }
    reverse(ret.dif2.begin(),ret.dif2.end());
    return ret;
}
void dfs(int u){
    if(!G[u].empty()){
        dfs(G[u].back()),swap(f[u],f[G[u].back()]),G[u].pop_back();
        for(int v:G[u]) dfs(v),f[u]=merge(f[u],f[v]);
    }
    else f[u].s0=0,f[u].negtot=f[u].f0=0,f[u].dif1.clear(),f[u].dif2.clear();
    if(str[u]=='1') f[u].f0++,f[u].negtot--,f[u].dif1.pb(-1);
    else f[u].dif2.pb(1);
    // cout<<f[u].f0<<" "<<f[u].negtot<<' '<<f[u].f0<<' '<<f[u].dif1.size()<<' '<<f[u].dif2.size()<<'\n';
    ans[u]=f[u].f0+f[u].negtot;
}
void work(){
    int n=read();scanf("%s",str+1);
    F(i,1,n) G[i].clear();
    F(i,2,n){ int fa=read();G[fa].pb(i);}
    dfs(1);
    F(i,1,n) printf("%d ",ans[i]);puts("");
}
int main(){
    int T=read();
    while(T--) work();
    fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
    return 0;
}

标签:cur,min,int,题解,差分,dif2,nanjing,len,ucup
From: https://www.cnblogs.com/Farmer-djx/p/17872758.html

相关文章

  • CF1288题解
    CF1288EducationalCodeforcesRound80(RatedforDiv.2)A略CF1288BlinkCF1288B题意给定\(A,B\),问有多少个二元组\((a,b)\)满足\(1\lea\leA,1\leb\leB\)且\(a\cdotb+a+b=\operatorname{conc}(a,b)\)。其中\(\operatorname{conc}(a,b)\)表示将\(a,b\)......
  • [ABC017D] サプリメント 题解
    题目传送门~一道DP前缀和优化好题。题目分析首先,朴素DP非常好想。可以从后向前考虑,设\(f_i\)表示从第\(i\)个补品开始的摄取方法数。摄取一个补品:\(f_i=f_{i+1}\)摄取两个补品:\(f_i=f_{i+2}\)以此类推。则转移方程为:\[f_i=f_{i+1}+f_{i+2}+\dots+f_{j......
  • P4143 采集矿石 题解
    题目传送门给出字符串\(s\),以及数组\(a_1\sima_{|s|}\)。定义一个子串的排名为:字典序比它大的本质不同的子串个数\(+1\)。定义一个子串\(s[l,r]\)的权值为\(\sum\limits_{i=l}^ra_i\)。求有多少个子串的排名等于权值。\(|s|\le10^5,0\lea_i\le1000\)。......
  • java: 未报告的异常错误java.io.UnsupportedEncodingException; 必须对其进行捕获或声
    原问题代码:/**MD5编码相关的类@authorwangjingtao*/publicclassMD5{//首先初始化一个字符数组,用来存放每个16进制字符privatestaticfinalchar[]hexDigits={'0','1','2','3','4','5','6','7'......
  • CF1790F题解
    思路令$dis_i$为离$i$最近的黑点距离,$ans$是距离最近的一对黑点距离,我们可以发现,每次$i\getsi+1$后$ans$的更新只会与$dis_{c_i}$有关,因为$c_i$是新的黑点,然后再从$c_i$来一次BFS更新$dis_i$即可。更详细的在注释。代码#include<bits/stdc++.h>......
  • [题解]AT_abc224_e [ABC224E] Integers on Grid
    比较符合CCF造数据水平的题。思路首先可以用两个vector<pair<int,int>>v[N]分别将每一行、每一列的元素的权值与编号存储下来。那么可以对所有的\(v_i\)按照权值从小到大排序。那么发现对于所有的满足v[i][p].fst<v[i][q].fst的\((p,q)\)都可以建一条从\(p\)指......
  • CSP第31次认证题解 2023.9
    A、坐标变换(其一)样例输入3210100010-201-100样例输出21-1120-10题解按照题目,一个循环即可#include<bits/stdc++.h>usingnamespacestd;#defineN200010#definelllonglongtemplate<classT>inlinevoidread(T&a){Tx=0,s=1;......
  • 使用Navicat For MSSQL连接绿色版SQLServer2008R2问题解决
    问题1、创建连接时出现错误:[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序(0)Navicat来连接SQLserver,这里确实有点麻烦,出现错误[IM002][Microsoft][ODBC驱动程序管理器]未发现数据源名称并且未指定默认驱动程序(0),解决方法:进入Navicat的安装......
  • CF1368题解
    CF1368CodeforcesGlobalRound8ABC略。CF1368DlinkCF1368D题意给定\(n\)个非负整数\(a_1,\cdots,a_n\)。你可以进行如下操作:选择两个不同的下标\(i,j\)满足\(1\leqi,j\leqn\),并将\(a_i\getsa_i\\mathsf{AND}\a_j,\a_j\getsa_i\\mathsf{OR}\a_j\),两个赋值......
  • newstarctf2023 reverse 题解汇总
    newstarctf2023reverse题解汇总week1easy_REdie查无壳64直接IDA启动跟到main函数找到两部分flag拼起来就行了。flag{we1c0me_to_rev3rse!!}ELFdie查64ELFIDA启动稍微读一下写个py逆一下它的加密就行了flag{D0_4ou_7now_wha7_ELF_1s?}importbase64a="VlxRV......