5+60+0=65。rk13。
今天去单位,被关在门外几分钟印象深刻。(想象一下捧着个笔记本接着机械键盘站在一家医院门口屏幕上显示"原谅")
$T1$看上去就是一个贪心,但是脑抽没有倒着想然后乱写搜索过样例骗了$5$分。
然而我觉得应该不止$5$分那么少吧。。。但是并没有心情去看一个指数级的搜索哪里写挂了。
$T2$大概是这场里最简单的一个,就是逐位考虑的模拟。
然而不止想麻烦了,而且树剖的$size$忘赋初值了(然而数据挺水没有卡)但最关键的是少考虑了一种情况于是挂乘$60$分。
然而到了$T3$就又没有多少时间了。乱写了一个树形$dp$然而写挂了。出于信仰并且表示自己曾经思考过于是交上去了。
改题也不顺。$T2$的那个细节吃了我不少时间。
在回家的车上瞥了一眼题解于是就在车上把$T1$打完了。(突然晕车)
$T3$的思路也不难明白,也不是很难写,但是数据比较强动不动就爆零,一个细节又吃掉了我一个晚上。
然后视频课又得咕到明天了。。
T1:倚天剑的愤怒
大意:$n$长序列,初始手上有一个值,依次可以选择是否加上序列的每个数,手上的数时刻非负,对于$m$种不同初始值回答最多加上序列的几个数。
$n,m le 10^6$
正难则反。倒着贪心。
遇到一个负数,就把它放到一个“待抵消列表”(堆)里。放入表示在最终的序列里不选。
如果遇到一个正数,那么就拿它去尽量多的抵消列表里的负数,肯定优先抵消绝对值小的那样抵消的多。
如果当前的这个正数不能把最小的元素抵消干净,那么就能抵消多少是多少,尽量减少它的值。
扫到头后堆里剩余元素就用初始值消于是就可以得到若干答案变化点。回答询问时在线二分复杂度$O((n+m)logn)$
当然也可以离线,指针扫一扫就好了,时间复杂度$O(nlogn+m)$
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 1000005 4 long long a[S],A,v[S];int n,m,ans[S]; 5 long long read(){ 6 register long long p=0,f=0;register char ch=getchar(); 7 while(!isdigit(ch))f=ch=='-',ch=getchar(); 8 while(isdigit(ch))p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 9 return f?-p:p; 10 } 11 priority_queue<int>q; 12 int main(){ 13 scanf("%d%d",&n,&m); 14 for(int i=1;i<=n;++i)a[i]=read(); 15 long long lft=0,c=0; 16 for(int i=n;i;--i) 17 if(a[i]>=0){ 18 lft+=a[i]; 19 while(!q.empty()&&lft+q.top()>=0)lft+=q.top(),q.pop(); 20 if(!q.empty()){int x=q.top();q.pop();q.push(lft+x);} 21 lft=0; 22 }else q.push(a[i]); 23 ans[c=1]=q.size(); 24 while(!q.empty())c++,v[c]=v[c-1]-q.top(),ans[c]=ans[c-1]-1,q.pop(); 25 v[++c]=1e17; 26 while(m-->0){ 27 long long x=read(); 28 printf("%d ",ans[upper_bound(v+1,v+1+c,x)-1-v]); 29 } 30 }
T2:原谅
大意:树点带权,要求选出不超过$K$个点权最大的点(任意一个选的都大于等于任意一个不选的),要求选出的点集联通。最大化点集。$n le 10^6$
离散化,权值从大到小扫。
依次考虑把当前权值的所有点都加入,那么它们到它们的$LCA$路径上所有的点都要被选。
找到这些最小值。设当前权值为$i$。如果最小值大于等于$i-1$那么就可以尽量多的添加$i-1$的点来更新答案。
理论上要特判权值最大的点都没选完的情况,但是数据里没有所以调试时给删掉了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 5000005 4 int n,fir[S],l[S],to[S],a[S],K,ec,r[S],LCA,sz[S],hson[S],dep[S],f[S],top[S],al[S],mn,now,ans; 5 vector<int>v[S]; 6 void link(int a,int b){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;} 7 void con(int a,int b){link(a,b);link(b,a);} 8 void dfs(int p,int fa){ 9 dep[p]=dep[fa]+1;f[p]=fa;sz[p]=1; 10 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 11 dfs(to[i],p);sz[p]+=sz[to[i]]; 12 if(sz[to[i]]>sz[hson[p]])hson[p]=to[i]; 13 } 14 } 15 void DFS(int p,int tp){ 16 top[p]=tp; 17 if(hson[p])DFS(hson[p],tp); 18 for(int i=fir[p];i;i=l[i])if(!top[to[i]])DFS(to[i],to[i]); 19 } 20 int lca(int x,int y){ 21 if(!x)return y; 22 while(top[x]!=top[y])if(dep[top[x]]>dep[top[y]])x=f[top[x]];else y=f[top[y]]; 23 return dep[x]>dep[y]?y:x; 24 } 25 void up(int p){for(;!al[p];p=f[p])al[p]=1,now++,mn=min(mn,a[p]);} 26 void extend(int p,int w){ 27 if(al[p]||now>=K)return;al[p]=1;mn=min(mn,a[p]);now++;LCA=lca(LCA,p); 28 for(int i=fir[p];i;i=l[i])if(!al[to[i]]&&a[to[i]]==w)extend(to[i],w); 29 } 30 int main(){//freopen("0.in","r",stdin); 31 scanf("%d%d",&n,&K); 32 for(int i=1;i<=n;++i)scanf("%d",&a[i]),r[i]=a[i]; 33 for(int i=1,a,b;i<n;++i)scanf("%d%d",&a,&b),con(a+1,b+1); 34 sort(r+1,r+1+n);int C=unique(r+1,r+1+n)-r-1; 35 for(int i=1;i<=n;++i)a[i]=lower_bound(r+1,r+1+C,a[i])-r,v[a[i]].push_back(i); 36 dfs(1,0);DFS(1,1);mn=C+1;al[0]=1; 37 for(int i=C;i;--i){ 38 int L=LCA; 39 for(int j=0;j<v[i].size();++j)LCA=lca(LCA,v[i][j]); 40 if(!al[LCA])al[LCA]=1,now++,mn=min(mn,a[LCA]); 41 if(L!=LCA)up(f[L]); 42 for(int j=0;j<v[i].size();++j)up(v[i][j]); 43 if(now>K)break; 44 if(mn>=i-1){ 45 for(int x=0;x<v[i-1].size();++x)if(!al[v[i-1][x]]) 46 for(int j=fir[v[i-1][x]];j;j=l[j])if(al[to[j]]) 47 {extend(v[i-1][x],i-1);break;} 48 ans=now; 49 } 50 }cout<<ans<<endl; 51 }
T3:收集
大意:树,每次某个点的选定状态取反,之后询问经过所有选定点的最短路径。$n,m le 10^6$
经历点集的最短路径?我们只要把起终点的最短路相连,那么形成的就是一个回路。每条边经过两次。
起终点是任取的,我们计算答案时要减掉它们之间的最短路,我们想最大化这个。
实际上就变成了关键点构成的直径以及所有关键点之间的边权和(可以称之为虚树?)。
分为两个问题了。
维护直径的话:建线段树,然后单次修改一个点肯定可以做到。然后就是线段树上的直径合并。
这个至少做过两道题了,$6$种情况分类讨论就是了。
如果写一个$O(1)LCA$的话复杂度就是$O(nlogn)$的。
也可以线段树分治,时间复杂度是$O(nlog^2n)$。
剩下的问题是虚树边权和。支持插入删除。
发现,每条边经过两次的一条回路,每个子树一定进出各一次。
于是发现$dfs$序满足这个性质。因为子树是连续区间所以左端点进右端点出。
所以只需要把所有关键点按照$dfs$序排序之后,求出相邻两元素以及首尾两元素的距离和即可。
只要用个$STLset$维护$dfs$序就好了。
线段树上还是要注意空节点的处理。。这东西调了好久
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define S 800005 4 int n,m,fir[S],l[S],to[S],ec,v[S],al[S],sz[S],f[S],dfn[S],a[S],b[S],tim,hson[S],top[S],idfn[S];long long dep[S],d[S],ans; 5 void link(int a,int b,int w){l[++ec]=fir[a];fir[a]=ec;to[ec]=b;v[ec]=w;} 6 void con(int a,int b,int w){link(a,b,w);link(b,a,w);} 7 void dfs(int p,int fa){ 8 sz[p]=1;f[p]=fa; 9 for(int i=fir[p];i;i=l[i])if(to[i]!=fa){ 10 dep[to[i]]=dep[p]+v[i];dfs(to[i],p); 11 sz[p]+=sz[to[i]]; if(sz[to[i]]>sz[hson[p]])hson[p]=to[i]; 12 } 13 } 14 void DFS(int p,int tp){ 15 dfn[p]=++tim;idfn[tim]=p;top[p]=tp; 16 if(hson[p])DFS(hson[p],tp); 17 for(int i=fir[p];i;i=l[i])if(!dfn[to[i]])DFS(to[i],to[i]); 18 } 19 long long dis(int a,int b){ 20 if(a*b==0)return 0; 21 long long ans=dep[a]+dep[b]; 22 while(top[a]!=top[b])if(dep[top[a]]>dep[top[b]])a=f[top[a]];else b=f[top[b]]; 23 return ans-2*min(dep[a],dep[b]); 24 } 25 void upd(int x,int p=1,int l=1,int r=n){ 26 if(l==r){a[p]^=x;b[p]^=x;return;} 27 int md=l+r>>1,lc=p<<1,rc=lc|1; 28 if(x<=md)upd(x,lc,l,md);else upd(x,rc,md+1,r); 29 if(!a[lc]){d[p]=d[rc],a[p]=a[rc],b[p]=b[rc];return;} 30 if(!b[rc]){d[p]=d[lc],a[p]=a[lc],b[p]=b[lc];return;} 31 long long aa=dis(a[lc],a[rc]),ab=dis(a[lc],b[rc]),ba=dis(b[lc],a[rc]),bb=dis(b[lc],b[rc]); 32 d[p]=max(max(max(max(max(d[lc],d[rc]),aa),ab),ba),bb); 33 if(d[p]==d[lc])a[p]=a[lc],b[p]=b[lc]; 34 if(d[p]==d[rc])a[p]=a[rc],b[p]=b[rc]; 35 if(d[p]==aa)a[p]=a[lc],b[p]=a[rc]; 36 if(d[p]==ab)a[p]=a[lc],b[p]=b[rc]; 37 if(d[p]==ba)a[p]=b[lc],b[p]=a[rc]; 38 if(d[p]==bb)a[p]=b[lc],b[p]=b[rc]; 39 } 40 set<int>s; 41 int main(){ 42 scanf("%d%d",&n,&m); 43 for(int i=1,a,b,w;i<n;++i)scanf("%d%d%d",&a,&b,&w),con(a,b,w); 44 dfs(1,0); DFS(1,1); 45 while(m--){ 46 int x;scanf("%d",&x);upd(x);al[x]^=1; 47 if(!al[x])s.erase(dfn[x]); 48 auto it=s.lower_bound(dfn[x]);int l,r; 49 if(s.empty())l=r=0; 50 else if(it==s.begin()||it==s.end())l=*s.begin(),r=*s.rbegin(); 51 else l=*it,r=*(--it); 52 if(al[x])s.insert(dfn[x]); 53 ans+=(al[x]?1:-1)*(dis(x,idfn[l])+dis(x,idfn[r])-dis(idfn[l],idfn[r])); 54 printf("%lld ",ans-d[1]); 55 } 56 }