高一上九月中旬日记
9.11
闲话
- 各学科的开学第一课宣讲,没心情听,故大部分时间都在颓《红楼梦》。
- 下午第一节课是历史,挺会忽悠人,物理宣讲完的大课间生奥教练来跟我们说信奥的直接去机房,其他奥赛在教室刷题。
- 下午最后一节课被 \(miaomiao\) 叫回去上语文自习。
- 晚上把答题器发下来了,比初中的在上面多了一个“鑫考股份”的 \(logo\) 。
- 晚三班主任喂了点鸡汤;分析了下换季时众人发烧、上吐下泻的原因(班主任声称是因早晚温差较大,新高一对于此事没有经验,与食堂没有半点关系),让我们加强体育锻炼。
做题纪要
9.12
闲话
- 补的昨天应该上的课(周二)。
- \(114\) 班被搬到楼梯旁的空教室了,隔壁教室也就成了空教室,于是作为奥赛教室和年级部的空教室使用。
- 上午选修改成数学自习了,所以数奥的可以上一上午奥赛,某数奥教练狂喜。
- 为 \(20 \min\) 左右的早预备再回趟教室不太值得,遂直接去机房了。到机房后 \(miaomiao\) 跟我们说以后早上第一节课是奥赛课早预备直接翘掉就行,还说今天早上要早知道我们来这么早还不如打一场模拟赛( \(3h\) 左右)。
- 下午大会因年级部深知把我们叫去就是为了凑人数的,和我们没太大关系,所以 \(201 \sim 203\) 三个奥赛班不用去开大会,正常上课表的课。
- 晚三初三 \(27\) 班班主任来班里转悠了会儿,喂了点鸡汤,督促我们抓紧时间学习、提高效率。
做题纪要
luogu P3806 【模板】点分治 1
-
若边权都为 \(1\) ,求出直径后判断即可。
-
点分治板子。
- 随意选择一个点作为根节点 \(rt\) ,则所以完全位于当前其子树内的路径以是否经过 \(rt\) 分为两种。而经过 \(rt\) 的路径 \(u \to v(u,v \ne rt)\) 又可以拆分成 \(u \to rt,rt \to v\) 两条路径。
- 此时满足 \(rt\) 是 \(u\) 和 \(v\) 的最近公共祖先。
- 考虑分治,对于枚举的 \(rt\) ,先计算在其子树内且经过 \(rt\) 的路径对答案产生的贡献,然后递归其子树对不经过该节点的路径进行求解。
- 点分治过程中,每一层的所有递归过程对每个点处理一次,时间复杂度为 \(O(hn)\) ,其中 \(h\) 为树高/递归层数。
- 若每次选择子树的中心作为根节点,可以保证递归层数最少,时间复杂度为 \(O(n \log n)\) 。
- 视询问情况决定是否让时间复杂度多乘上一个 \(O(m)\) 。
- 在重新选择根节点后一定要重新计算子树的大小。
- 随意选择一个点作为根节点 \(rt\) ,则所以完全位于当前其子树内的路径以是否经过 \(rt\) 分为两种。而经过 \(rt\) 的路径 \(u \to v(u,v \ne rt)\) 又可以拆分成 \(u \to rt,rt \to v\) 两条路径。
-
对于经过根节点 \(rt\) 的路径,先计算出 \(x\) 子树内节点 \(x\) 到根节点的距离 \(dis_{x}\) , \(judge_{d}\) 表示之前处理的子树中是否存在长度为 \(d\) 的路径。若一个询问的 \(k\) 满足 \(judge_{k-dis_{x}}=1\) 则说明存在一条长度为 \(k\) 的路径。处理完一棵子树的询问后及时更新 \(judge\) 。
-
清空 \(judge\) 数组时必须手动清空,否则使用
memset
清空会导致时间复杂度错误。- 具体地,将占用过的 \(judge\) 的位置加入一个队列里,然后进行清空。
-
时间复杂度为 \(O(nm \log n)\) ,空间复杂度为 \(O(V)\) 。
- 由于 \(k \le 10^{7}\) , \(judge\) 和队列中只保存 \(\le 10^{7}\) 的数,剩下的数一定不可能对答案产生贡献。
点击查看代码
struct node { int nxt,to,w; }e[20010]; int head[20010],ask[20010],ans[20010],cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct Divide_On_Tree { int siz[20010],weight[20010],vis[20010],dis[20010],tmp[20010],judge[10000010],center; queue<int>q; void init(int n,int m) { center=0; get_center(1,0,n);//求重心 get_siz(center,0);//更新大小(单独写一个函数来减小常数) divide(center,m); } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0)//因为遍历子树的需要,需将判父亲和 vis 相结合 { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(int x,int fa) { siz[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(int x,int fa) { tmp[0]++;//方便便利整颗子树的所有节点的 dis tmp[tmp[0]]=dis[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=dis[x]+e[i].w; get_dis(e[i].to,x); } } } void divide(int x,int m) { judge[0]=1; q.push(0); vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0)//因为有了重心的参与单纯判断父亲节点不再可做 { tmp[0]=0; dis[e[i].to]=e[i].w; get_dis(e[i].to,x); for(int j=1;j<=tmp[0];j++) { for(int k=1;k<=m;k++) { if(ask[k]>=tmp[j]) { ans[k]|=judge[ask[k]-tmp[j]]; } } } for(int j=1;j<=tmp[0];j++) { if(tmp[j]<=10000000) { q.push(tmp[j]); judge[tmp[j]]=1; } } } } while(q.empty()==0) { judge[q.front()]=0; q.pop(); } for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center,m); } } } }T; int main() { int n,m,u,v,w,i; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } for(i=1;i<=m;i++) { cin>>ask[i]; } T.init(n,m); for(i=1;i<=m;i++) { if(ans[i]==1) { cout<<"AYE"<<endl; } else { cout<<"NAY"<<endl; } } return 0; }
luogu P4178 Tree
-
多倍经验: luogu P10461 Tree
-
点分治套一个支持单点修改区间查询的数据结构即可。
点击查看代码
struct node { int nxt,to,w; }e[80010]; int head[80010],ask,ans,cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct BIT { int c[20010]; int lowbit(int x) { return (x&(-x)); } void add(int n,int x,int val) { for(int i=x;i<=n;i+=lowbit(i)) { c[i]+=val; } } int getsum(int x) { int ans=0; for(int i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; }; }T; struct Divide_On_Tree { int siz[80010],weight[80010],vis[80010],dis[80010],tmp[80010],center; queue<int>q; void init(int n) { get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(int x,int fa) { siz[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(int x,int fa) { tmp[0]++; tmp[tmp[0]]=dis[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=dis[x]+e[i].w; get_dis(e[i].to,x); } } } void divide(int x) { T.add(20001,0+1,1); q.push(0); vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { tmp[0]=0; dis[e[i].to]=e[i].w; get_dis(e[i].to,x); for(int j=1;j<=tmp[0];j++) { if(ask>=tmp[j]) { ans+=T.getsum(ask-tmp[j]+1); } } for(int j=1;j<=tmp[0];j++) { if(tmp[j]<=20000) { T.add(20001,tmp[j]+1,1); q.push(tmp[j]); } } } } while(q.empty()==0) { T.add(20001,q.front()+1,-1); q.pop(); } for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center); } } } }D; int main() { int n,u,v,w,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } cin>>ask; D.init(n); cout<<ans<<endl; return 0; }
CF161D Distance in Tree
-
单点修改单点查询。
点击查看代码
struct node { int nxt,to,w; }e[100010]; int head[100010],ask,ans,cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct Divide_On_Tree { int siz[100010],weight[100010],vis[100010],dis[100010],tmp[100010],judge[100010],center; queue<int>q; void init(int n) { center=0; get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(int x,int fa) { siz[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(int x,int fa) { tmp[0]++; tmp[tmp[0]]=dis[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=dis[x]+e[i].w; get_dis(e[i].to,x); } } } void divide(int x) { judge[0]=1; q.push(0); vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { tmp[0]=0; dis[e[i].to]=e[i].w; get_dis(e[i].to,x); for(int j=1;j<=tmp[0];j++) { if(ask>=tmp[j]) { ans+=judge[ask-tmp[j]]; } } for(int j=1;j<=tmp[0];j++) { q.push(tmp[j]); judge[tmp[j]]++; } } } while(q.empty()==0) { judge[q.front()]=0; q.pop(); } for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center); } } } }D; int main() { int n,u,v,i; cin>>n>>ask; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v,1); add(v,u,1); } D.init(n); cout<<ans<<endl; return 0; }
9.13
闲话
- 上午第四节课体育改成公自了,并和第五节课的数学自习换了换。
- 下午大课间时,班主任说若大课间前后有奥赛课,那么团体操就不用下去跳了,有他和年级部交涉。
- 到机房后 \(miaomiao\) 问了下我们的刷题情况,速度如何,暑假讲的知识点写的怎么样了。然后说了下高二的等考完 \(CSP\) 初赛就开始集训,我们则先巩固好高一基础,等国庆放假回来再集训,等考完 \(CSP\) 后统一学一周 \(whk\) ,然后开始 \(NOIP\) 集训;集训期间班里发的资料要自己回去去拿,且保留好,建议我们每天轮流去教室统一拿所有人的,别像 \(HZOI2022\) 一样发的资料现在还在机房留着呢, \(miaomiao\) 本想当废纸卖但鉴于废纸价格太便宜,卖了之后可能还不够我们吃一顿雪糕的钱,所以就先留着; \(NOIP\) 在 \(11.30\) 和 \(11.23\) 之间反复横跳,但最后定在了 \(11.30\) ,说距离 \(NOIP\) 的时间还多着,让我们不要着急。
- 晚新闻说了国家助学金的事情,让每班上报人数 \(\in [6,10]\) ,评选资格不那么严格,但没给我们说明白开什么证明。
- 晚三班主任补充了下国家助学金的事情,说 HZ 每年都报不满,让我们“能报就报,把国家给你发的钱都赚下来”;并说最近可能又是流感泛滥,全衡水都是这样发烧/上吐下泻的,让我们能坚持就坚持,如果要出校就等一次性治好了再回来。
做题纪要
luogu P2634 [国家集训队] 聪聪可可
-
分母显然为 \(n^{2}\) 。
-
以距离 \(\bmod 3\) 后的值插入桶内,点分治维护距离为 \(2\) 的点对数量,最后乘 \(2\) (顺序影响答案)再加上 \(n\) (自身到自身也算)即可。
点击查看代码
struct node { int nxt,to,w; }e[40010]; int head[40010],ans1,ans2,cnt=0; void add(int u,int v,int w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } int gcd(int a,int b) { return b?gcd(b,a%b):a; } struct Divide_On_Tree { int siz[40010],weight[40010],vis[40010],dis[40010],tmp[40010],judge[5],center; queue<int>q; void init(int n) { center=0; get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(int x,int fa) { siz[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(int x,int fa) { tmp[0]++; tmp[tmp[0]]=dis[x]; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=(dis[x]+e[i].w)%3; get_dis(e[i].to,x); } } } void divide(int x) { judge[0]=1; q.push(0); vis[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { tmp[0]=0; dis[e[i].to]=e[i].w%3; get_dis(e[i].to,x); for(int j=1;j<=tmp[0];j++) { ans1+=judge[(3-tmp[j])%3]; } for(int j=1;j<=tmp[0];j++) { q.push(tmp[j]); judge[tmp[j]]++; } } } while(q.empty()==0) { judge[q.front()]=0; q.pop(); } for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center); } } } }D; int main() { int n,u,v,w,d,i; cin>>n; ans2=n*n; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w%3); add(v,u,w%3); } D.init(n); ans1=ans1*2+n; d=gcd(ans1,ans2); ans1/=d; ans2/=d; cout<<ans1<<"/"<<ans2<<endl; return 0; }
luogu P4149 [IOI2011] Race
-
额外开一个数组记录途中边的数量即可。
点击查看代码
struct node { ll nxt,to,w; }e[400010]; ll head[400010],ask,ans=0x7f7f7f7f,cnt=0; void add(ll u,ll v,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct Divide_On_Tree { ll siz[400010],weight[400010],vis[400010],dis[400010],tmp[400010],d[400010],judge[1000010],center; queue<ll>q; void init(ll n) { memset(judge,0x3f,sizeof(judge)); center=0; get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(ll x,ll fa,ll n) { siz[x]=1; weight[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(ll x,ll fa) { siz[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(ll x,ll fa,ll dep) { tmp[0]++; tmp[tmp[0]]=dis[x]; d[tmp[0]]=dep; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=dis[x]+e[i].w; get_dis(e[i].to,x,dep+1); } } } void divide(ll x) { judge[0]=0; q.push(0); vis[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { tmp[0]=0; dis[e[i].to]=e[i].w; get_dis(e[i].to,x,1); for(ll j=1;j<=tmp[0];j++) { if(ask>=tmp[j]) { ans=min(ans,d[j]+judge[ask-tmp[j]]); } } for(ll j=1;j<=tmp[0];j++) { if(tmp[j]<=1000000) { q.push(tmp[j]); judge[tmp[j]]=min(judge[tmp[j]],d[j]); } } } } while(q.empty()==0) { judge[q.front()]=0x3f3f3f3f3f3f3f3f; q.pop(); } for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center); } } } }D; int main() { ll n,u,v,w,i; cin>>n>>ask; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; u++; v++; add(u,v,w); add(v,u,w); } D.init(n); cout<<(ans==0x7f7f7f7f?-1:ans)<<endl; return 0; }
9.14
闲话
- 因为有高考研讨会,早上让整理内务,女生宿舍楼因为条件“优越”所以所为开放区,男生宿舍怕开放得不过瘾所以教务处去查内务。集合时间延后了 \(2 \min\) 。
- 早饭时间延后了 \(10 \min\) ,且全部被赶到了一楼吃饭(二楼留给高考研讨会人员吃饭用)。
- 上午到机房后 \(field\) 跟我们说 \(505\) 不只有我们在用, \(203\) 信奥的也在用这个机房,让我们把桌面收拾好点,按时保存代码,明天这里还有可能给公益课的用;找 @K8He 交流了下 求调RE ,最后得到结果是爆递归栈(超过题目中所说的 \(1GB\) )了; @wkh2008 让 @wang54321 去找 \(miaomiao\) 问晚上能不能来打 \(ABC\) , \(miaomiao\) 说我们班主任同意就行。
- 午饭时间提前了 \(15 \min\) ,但班主任让 \(12:15\) 回班学习。约 \(12:25\) 时班主任从门外经过了一下,但没进班,估计去进了备课区。但貌似 @wang54321 和 @HANGRY_sol 去找班主任问了下晚上能不能打 \(ABC\) ,班主任同意了。
- 下午大课间没有去跳团体操。
- 晚上下了物理自习是 \(19:55\) ,就赶紧跑到机房报名 \(ABC\) 了,在 \(19:58\) 报上了 \(rated\) 。到机房一楼时某化学老师刚从电梯里出来,带着一堆实验用品(目测是试管和一堆乱七八糟的东西),当时我, @wkh2008 和 @ccxswl 恰巧从旁边经过,还问我们“同学,你现在忙吗?”,估计是想让我们去当“苦力”。到机房后 \(feifei\) 进来问了下我们这节是啥课,请假了吗,得到回复后就走了。
- 数学学案、自助,语文习字,生物笔记还没写,祝我好运。
做题纪要
牛客 NC276527 Cidoai的幂次序列
-
构造 \(m=2,a_{0}=1,a_{1}=n-1\) 即可。
点击查看代码
int main() { ll n,k; cin>>n>>k; cout<<2<<endl; cout<<1<<" "<<n-1<<endl; return 0; }
牛客 NC276571 Cidoai的平均数对
-
\(\le k\) 的选了一定更优,与 \(k\) 的差值即是背包容量。
-
将 \(>k\) 的 \(b\) 减去 \(k\) 后就是 \(01\) 背包板子了。
点击查看代码
int f[500010],a[510],b[510]; vector<pair<int,int> >c; int main() { int n,k,m=0,ans=0,i,j; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]>>b[i]; if(b[i]<=k) { ans+=a[i]; m+=k-b[i]; } else { c.push_back(make_pair(a[i],b[i]-k)); } } f[0]=0; for(i=0;i<c.size();i++) { for(j=m;j>=c[i].second;j--) { f[j]=max(f[j],f[j-c[i].second]+c[i].first); } } cout<<f[m]+ans<<endl; return 0; }
牛客 NC276573 Cidoai的树上方案
-
观察到一个点和父亲节点不能同时选,做法同 luogu P1352 没有上司的舞会 。
点击查看代码
const ll p=998244353; struct node { ll nxt,to; }e[2000010]; ll head[2000010],f[2000010][2],cnt=0; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(ll x) { f[x][0]=f[x][1]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { dfs(e[i].to); f[x][0]=f[x][0]*((f[e[i].to][0]+f[e[i].to][1])%p)%p; f[x][1]=f[x][1]*f[e[i].to][0]%p; } } int main() { ll n,u,v,i; cin>>n; for(i=2;i<=n;i++) { cin>>u; v=i; add(u,v); } dfs(1); cout<<(f[1][0]+f[1][1])%p<<endl; return 0; }
[ABC371A] Jiro
-
选择结构。
点击查看代码
int main() { char a,b,c; cin>>a>>b>>c; if(a=='<'&&b=='<'&&c=='<') { cout<<"B"<<endl; } if(a=='<'&&b=='<'&&c=='>') { cout<<"C"<<endl; } if(a=='<'&&b=='>'&&c=='>') { cout<<"A"<<endl; } if(a=='>'&&b=='<'&&c=='<') { cout<<"A"<<endl; } if(a=='>'&&b=='>'&&c=='<') { cout<<"C"<<endl; } if(a=='>'&&b=='>'&&c=='>') { cout<<"B"<<endl; } return 0; }
[ABC371B] Taro
-
模拟。
点击查看代码
int vis[110]; int main() { int n,m,a,i; char s; cin>>n>>m; for(i=1;i<=m;i++) { cin>>a>>s; if(s=='M'&&vis[a]==0) { vis[a]=1; cout<<"Yes"<<endl; } else { cout<<"No"<<endl; } } return 0; }
[ABC371D] 1D Country
-
离散化后做前缀和即可。
点击查看代码
ll x[200010],p[200010],a[200010],sum[200010]; int main() { ll n,q,l,r,i; cin>>n; for(i=1;i<=n;i++) { cin>>x[i]; a[i]=x[i]; } sort(a+1,a+1+n); a[0]=unique(a+1,a+1+n)-(a+1); for(i=1;i<=n;i++) { cin>>p[i]; sum[lower_bound(a+1,a+1+a[0],x[i])-a]+=p[i]; } for(i=1;i<=a[0];i++) { sum[i]+=sum[i-1]; } cin>>q; for(i=1;i<=q;i++) { cin>>l>>r; l=lower_bound(a+1,a+1+a[0],l)-a; r=upper_bound(a+1,a+1+a[0],r)-a-1; if(l>r) { cout<<"0"<<endl; } else { cout<<sum[r]-sum[l-1]<<endl; } } return 0; }
[ABC371E] I Hate Sigma Problems
-
若每个位置的颜色互不相同,那么就是 普及模拟1 T1 Past 的第一问了。
-
考虑统计每个位置对它所在的区间产生贡献的次数,这样的话一个区间就会被统计它的长度次,若这个区间内有相同颜色的只能统计一次,不妨让区间内同一颜色的点只产生一次贡献。
-
记 \(last_{a_{i}}\) 表示 \(a_{i}\) 上一次出现的位置,则 \(\sum\limits_{i=1}^{n}(i-last_{a_{i}})(n-i+1)\) 即为所求。
点击查看代码
ll a[200010],last[200010]; int main() { ll n,ans=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; ans+=(n-i+1)*(i-last[a[i]]); last[a[i]]=i; } cout<<ans<<endl; return 0; }
[ABC371C] Make Isomorphic
-
观察到 \(n \le 8\) ,暴力判断重构即可。
点击查看代码
ll u[500],v[500],mc[20][20],gc[20][20],p[20],c[20],a[20][20]; int main() { ll n,mg,mh,x,y,ans=0x7f7f7f7f,sum=0,i,j; cin>>n>>mg; for(i=1;i<=mg;i++) { cin>>x>>y; gc[x][y]=gc[y][x]=1; } cin>>mh; for(i=1;i<=mh;i++) { cin>>u[i]>>v[i]; } for(i=1;i<=n-1;i++) { for(j=i+1;j<=n;j++) { cin>>a[i][j]; } } for(i=1;i<=n;i++) { p[i]=i; } do { sum=0; memset(mc,0,sizeof(mc)); for(i=1;i<=n;i++) { c[p[i]]=i; } for(i=1;i<=mh;i++) { mc[p[u[i]]][p[v[i]]]=mc[p[v[i]]][p[u[i]]]=1; } for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { sum+=(mc[i][j]!=gc[i][j])*max(a[c[i]][c[j]],a[c[j]][c[i]]); } } ans=min(ans,sum); }while(next_permutation(p+1,p+1+n)); cout<<ans<<endl; return 0; }
9.15
闲话
- 早上貌似不跑操,让 \(6:20\) 到教室。因为是周日,起床铃没响。
- 早预备的时候班主任让我们下去拍了班级照。回班后把语文习字写了,数学学案写了一半;得知了旁边的空教室等国庆回来可能要给西藏班的用,到国庆前让我们顺带值下楼道卫生。
- 上数学课时讲题时顺带着把数学学案剩下的部分写完了。
- 上午第五节课原本是信息,不知道为啥改成阅览了。遂颓下发的杂志。
- 下午起床后直接去机房了。
- @jijidawang 问 \(miaomiao\) 能不能解决 luogu 封学校 IP 的事情,反被 \(miaomiao\) 反问是他搞的吗。
做题纪要
[ABC371F] Takahashi in Narrow Road
-
由题,每次操作会让序列中一段数变成公差为 \(1\) 的等差数列。
-
考虑让第 \(i(i \in [1,n])\) 个数减去 \(i\) ,那么在同一个等差数列里的各个位置上的数都相等。
-
二分移动后的左右端点,线段树维护区间赋值。
点击查看代码
ll x[800010]; struct SMT { struct SegmentTree { ll l,r,sum,maxx,lazy; }tree[800010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void pushup(ll rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void build(ll rt,ll l,ll r) { tree[rt].l=l; tree[rt].r=r; tree[rt].lazy=-0x7f7f7f7f; if(l==r) { tree[rt].maxx=tree[rt].sum=x[l]; return; } ll mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(ll rt) { if(tree[rt].lazy!=-0x7f7f7f7f) { tree[lson(rt)].lazy=tree[rson(rt)].lazy=tree[rt].lazy; tree[lson(rt)].maxx=tree[rson(rt)].maxx=tree[rt].lazy; tree[lson(rt)].sum=tree[rt].lazy*(tree[lson(rt)].r-tree[lson(rt)].l+1); tree[rson(rt)].sum=tree[rt].lazy*(tree[rson(rt)].r-tree[rson(rt)].l+1); tree[rt].lazy=-0x7f7f7f7f; } } void update(ll rt,ll x,ll y,ll val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].lazy=tree[rt].maxx=val; tree[rt].sum=val*(tree[rt].r-tree[rt].l+1); return; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2; if(x<=mid) { update(lson(rt),x,y,val); } if(y>mid) { update(rson(rt),x,y,val); } pushup(rt); } ll query_sum(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query_sum(lson(rt),x,y); } if(y>mid) { ans+=query_sum(rson(rt),x,y); } return ans; } ll query_max(ll rt,ll x,ll y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].maxx; } pushdown(rt); ll mid=(tree[rt].l+tree[rt].r)/2,ans=-0x7f7f7f7f; if(x<=mid) { ans=max(ans,query_max(lson(rt),x,y)); } if(y>mid) { ans=max(ans,query_max(rson(rt),x,y)); } return ans; } }T; int main() { ll n,m,t,g,pos,l,r,mid,ans,sum=0,i; cin>>n; for(i=1;i<=n;i++) { cin>>x[i]; x[i]-=i; } T.build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>t>>g; pos=T.query_max(1,t,t); if(pos!=g-t) { if(pos<g-t) { l=t+1; r=n; ans=t; while(l<=r) { mid=(l+r)/2; if(T.query_max(1,t,mid)>g-t) { r=mid-1; } else { l=mid+1; ans=mid; } } sum+=(g-t)*(ans-t+1)-T.query_sum(1,t,ans); T.update(1,t,ans,g-t); } else { l=1; r=t-1; ans=t; while(l<=r) { mid=(l+r)/2; if(T.query_max(1,1,mid)<g-t) { l=mid+1; } else { r=mid-1; ans=mid; } } sum+=T.query_sum(1,ans,t)-(g-t)*(t-ans+1); T.update(1,ans,t,g-t); } } } cout<<sum<<endl; return 0; }
9.16
闲话
- 早操前听德育主任叭叭了堆东西。比较重要的就是违纪生延迟放假 \(4h\) 左右去听整改大会,但班主任可以代替。
- 上午两节公自,一节课用来颓《红楼梦》,一节课用来补生物笔记。
- 下午还有节公自,但因为放假回来没有数学自习了,遂只能这节公自改成数学自习了。数奥的还被班主任拉过去上了 \(30 \min\) 课;快下课时班主任说了下要求,说班里违纪生的整改大会不用去听了,他替我们去(实际上他也不听)。
- 然后放假,从打下课铃开始算是整两天假期,我们 HZ 太强了。
- 晚上颓。
- 到家后发现原人人要做的德育作业被分给了数奥的做,遂没再管德育作业。
做题纪要
9.17
闲话
- 体育选课,因为只能选一门且整个学期都要学这一门,遂选的足球。
- 颓。
做题纪要
9.18
闲话
- 上午颓。
- 下午返校。可能是因为从一部应该进的门口进的缘故,原拦截行李箱的人员没有把我拦下来,只听见一个老师在向旁边的一部年级主任报告这件事,但没听见一部年级主任说了啥。到宿舍后发现学校以行李箱是违禁品,严重危害学习的名义禁止带行李箱,所有行李箱不能放在宿舍里,已经带进学校的需要让家长带走,并在校门口进行拍照加罚站。本来想把行李箱拿去机房的但还是决定和家长打电话再回学校把行李箱带走。在校门口等的时候(换了个门口),被年级干事拦下来罚站,发现带行李箱的基本都是 \(201,202\) 的,然后让我们一起把行李箱统一放到年级部去,我因为在等家长所以没跟着一起去。
- 到班后从班主任的口中得知鑫考云给家长发了短信提示禁止带行李箱的事情,但我们都不知情,离谱。
- 直接上周三的课,遂来机房了。
- 到机房后 \(feifei\) 过来转了圈问我们这节是啥课,然后收了下手机。过了一会儿, \(miaomiao\) 又来了收了下手机,挨个问了下带手机但没交给他的人。
- 晚三班主任以 \(12:10\) 的窗口排队情况驳斥(驴唇不对马嘴)了我们所说的“吃不上饭”,于是让我们中午留到 \(12:10\) 再去吃饭。
做题纪要
luogu P4138 [JOISC2014] 挂饰
-
设 \(f_{i,j}\) 表示前 \(i\) 个挂饰中共有 \(j\) 个挂钩的最大价值。背包处理即可。
-
背包有用容量至多为 \(n\) 来保证时空复杂度正确。
-
优先转移挂钩数量多的挂饰来保证没有后效性。
点击查看代码
int f[2010][2010]; pair<int,int>a[2010]; int main() { int n,ans=-0x7f7f7f7f,i,j; cin>>n; for(i=1;i<=n;i++) { cin>>a[i].first>>a[i].second; } sort(a+1,a+1+n); memset(f,-0x3f,sizeof(f)); f[n+1][1]=0; for(i=n;i>=1;i--) { for(j=0;j<=n;j++) { f[i][j]=max(f[i+1][j],f[i+1][max(j+1-a[i].first,1)]+a[i].second); } } for(i=0;i<=n;i++) { ans=max(ans,f[1][i]); } cout<<ans<<endl; return 0; }
9.19
闲话
- 上午后三节课都是奥赛,遂中午直接去吃饭了。
- 下午大课间的团体操因为下一节课是数学课,所以数奥的被拉去上课了,不用下去跳操,我们也就不用下去跳操了。
- 下午第九节课被拉下去拍档案的学生照片了。
- 晚三被全体拉下去练团体操,说要为运动会开幕式做准备(两个都要练),每人下发了两个荧光棒作为道具使用。结束时还声称到运动会开幕式前的下午团体操都要练习,下周一、周二的晚三也要被拉下去练习。
做题纪要
luogu P3085 [USACO13OPEN] Yin and Yang G
-
同 luogu P3564 [POI2014] BAR-Salad Bar ,考虑令黑色的权值为 \(-1\) ,白色的权值为 \(1\) 。
-
设 \(dis_{x}\) 表示 \(x\) 到此时枚举的子树重心 \(rt\) 的距离。则 \(x \to y\) 合法当且仅当在 \(dis_{x}=-dis_{y}\) 的情况下存在一个 \(x \to y\) 上的点 \(z\) 使得 \(z\) 是 \(x\) 的祖先且 \(dis_{z}=dis_{y}\) 或 \(z\) 是 \(y\) 的祖先且 \(dis_{z}=dis_{x}\) 。
-
设 \(f_{x,0/1}/g_{x,0/1}\) 分别表示当前子树/已经遍历的子树内有/无祖先节点和自己的 \(dis\) 同时等于 \(x\) 的点的数量。统计答案时进行配对即可。
-
注意特判重心是断点的情况。
点击查看代码
struct node { ll nxt,to,w; }e[200010]; ll head[200010],ans,cnt=0; void add(ll u,ll v,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } struct Divide_On_Tree { ll siz[200010],weight[200010],vis[200010],tmp[200010],dis[200010],f[200010][2],g[200010][2],center,dep; void init(ll n) { center=0; get_center(1,0,n); get_siz(center,0); divide(center,n); } void get_center(ll x,ll fa,ll n) { siz[x]=1; weight[x]=0; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_center(e[i].to,x,n); siz[x]+=siz[e[i].to]; weight[x]=max(weight[x],siz[e[i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(ll x,ll fa) { siz[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { get_siz(e[i].to,x); siz[x]+=siz[e[i].to]; } } } void get_dis(ll x,ll fa,ll de) { dep=max(dep,de); f[dis[x]][(tmp[dis[x]]>=1)]++; tmp[dis[x]]++; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { dis[e[i].to]=dis[x]+e[i].w; get_dis(e[i].to,x,de+1); } } tmp[dis[x]]--; } void divide(ll x,ll n) { ll maxx=0; vis[x]=1; g[0+n][0]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { dep=0; dis[e[i].to]=n+e[i].w; get_dis(e[i].to,x,1); maxx=max(maxx,dep); ans+=f[0+n][0]*(g[0+n][0]-1); for(ll j=-dep;j<=dep;j++) { ans+=f[n+j][0]*g[n-j][1]+f[n+j][1]*g[n-j][0]+f[n+j][1]*g[n-j][1]; } for(ll j=-dep;j<=dep;j++) { g[n+j][0]+=f[n+j][0]; g[n+j][1]+=f[n+j][1]; f[n+j][0]=f[n+j][1]=0; } } } for(ll i=-maxx;i<=maxx;i++) { g[n+i][0]=g[n+i][1]=0; } for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { center=0; get_center(e[i].to,x,siz[e[i].to]); get_siz(center,0); divide(center,n); } } } }D; int main() { ll n,u,v,w,i; cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w-(w==0)); add(v,u,w-(w==0)); } D.init(n); cout<<ans<<endl; return 0; }
9.20
闲话
- 侯操时得知了 \(10.10 \sim 10.11\) 进行一调考试的事情,这下大抵是能逃一调了,赢。
- 上午大课间的时候开始刮风,掉了一堆叶子,室外值日在仅有两人的情况下根本就没有方法进行。
- 下午到机房后看见了机房门口的金属检测器和信号屏蔽器,还挺正式;快下课的时候 \(miaomiao\) 跟我们说让我们刷刷初赛题,然后我们发现初赛知识点啥都不会,尝试跟 \(miaomiao\) 交涉明天上午能不能来机房进行初赛集训,但 \(miaomiao\) 以有公益课的要占用机房拒绝了;大课间的时候听劳委说叫了 \(5\) 个人扫了 \(20 \min\) 才扫完落叶。
- 吃晚饭前下雨了,遂室外卫生区不用值日了。
- 晚新闻的时候被 \(miaomiao\) 以“要说事”叫出来让去机房。去机房的路上看见了 \(CSP\) 的海报;进楼后尝试坐电梯,但又被 \(miaomiao\) 赶下来了,然后我们“刷了 \(miaomiao\) 的副本”;到机房后发现了另外的海报,通过 \(feifei\) 的言行可以猜测出貌似机房成考点了,离谱。
做题纪要
luogu P3714 [BJOI2017] 树的难题
-
显然两条路径可以合并,特判下路径端点是否相等即可。
-
考虑两棵线段树分别维护遍历过的不同颜色的最大权值和与当前颜色的最大权值和(以深度为下标)。更换颜色时暴力合并两棵线段树即可。
-
将与一个点相连的所有边按照颜色升序排序,方便相同颜色的边一起处理。
-
对于同一深度仅记录最大权值和,遍历时只遍历一遍深度即可。
-
对于清空整颗线段树维护一个区间覆盖的懒惰标记即可。
点击查看代码
const int inf=0x3f3f3f3f; struct node { int to,col; }; int L,R,c[200010],ans=-inf,cnt=0; vector<node>e[200010]; bool cmp(node a,node b) { return a.col<b.col; } void add(int u,int v,int w) { e[u].push_back(node{v,w}); } struct SMT { struct SegmentTree { int l,r,maxx,lazy; }tree[200010<<2]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].maxx=max(tree[lson(rt)].maxx,tree[rson(rt)].maxx); } void clear() { tree[1].lazy=1; tree[1].maxx=-inf; } void build(int rt,int l,int r) { tree[rt].l=l; tree[rt].r=r; tree[rt].lazy=0; if(l==r) { tree[rt].maxx=-inf; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void pushdown(int rt) { if(tree[rt].lazy==1) { tree[lson(rt)].lazy=tree[rson(rt)].lazy=tree[rt].lazy; tree[lson(rt)].maxx=tree[rson(rt)].maxx=-inf; tree[rt].lazy=0; } } void update(int rt,int pos,int val) { if(tree[rt].l==tree[rt].r) { tree[rt].maxx=max(tree[rt].maxx,val); return; } pushdown(rt); int mid=(tree[rt].l+tree[rt].r)/2; if(pos<=mid) { update(lson(rt),pos,val); } else { update(rson(rt),pos,val); } pushup(rt); } int query(int rt,int x,int y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].maxx; } pushdown(rt); int mid=(tree[rt].l+tree[rt].r)/2,ans=-inf; if(x<=mid) { ans=max(ans,query(lson(rt),x,y)); } if(y>mid) { ans=max(ans,query(rson(rt),x,y)); } return ans; } }same,diff; struct Divide_On_Tree { int siz[200010],weight[200010],vis[200010],f[200010],tmp[200010],center,dep; void init(int n) { same.build(1,0,R); diff.build(1,0,R); memset(f,-0x3f,sizeof(f)); memset(tmp,-0x3f,sizeof(tmp)); tmp[0]=center=0; get_center(1,0,n); get_siz(center,0); divide(center); } void get_center(int x,int fa,int n) { siz[x]=1; weight[x]=0; for(int i=0;i<e[x].size();i++) { if(e[x][i].to!=fa&&vis[e[x][i].to]==0) { get_center(e[x][i].to,x,n); siz[x]+=siz[e[x][i].to]; weight[x]=max(weight[x],siz[e[x][i].to]); } } weight[x]=max(weight[x],n-siz[x]); if(weight[x]<=n/2) { center=x; } } void get_siz(int x,int fa) { siz[x]=1; for(int i=0;i<e[x].size();i++) { if(e[x][i].to!=fa&&vis[e[x][i].to]==0) { get_siz(e[x][i].to,x); siz[x]+=siz[e[x][i].to]; } } } void get_dis(int x,int fa,int val,int de,int col,int flag) { if(de<=R) { dep=max(dep,de); val+=flag*c[col]; f[de]=max(f[de],val); for(int i=0;i<e[x].size();i++) { if(e[x][i].to!=fa&&vis[e[x][i].to]==0) { get_dis(e[x][i].to,x,val,de+1,e[x][i].col,e[x][i].col!=col); } } } } void divide(int x) { int col=0; vis[x]=1; diff.update(1,0,0); for(int i=0;i<e[x].size();i++) { if(vis[e[x][i].to]==0) { if(e[x][i].col!=col) { for(int j=1;j<=tmp[0];j++) { diff.update(1,j,tmp[j]); tmp[j]=-inf; } same.clear(); tmp[0]=0; } dep=0; get_dis(e[x][i].to,x,0,1,e[x][i].col,1); for(int j=1;j<=dep;j++) { ans=max(ans,f[j]+diff.query(1,max(L-j,0),R-j)); if(e[x][i].col==col) { ans=max(ans,f[j]+same.query(1,max(L-j,0),R-j)-c[col]); } } tmp[0]=max(tmp[0],dep); col=e[x][i].col; for(int j=1;j<=dep;j++) { tmp[j]=max(tmp[j],f[j]); same.update(1,j,tmp[j]); f[j]=-inf; } } } same.clear(); diff.clear(); for(int i=1;i<=tmp[0];i++) { tmp[i]=-inf; } tmp[0]=0; for(int i=0;i<e[x].size();i++) { if(vis[e[x][i].to]==0) { center=0; get_center(e[x][i].to,x,siz[e[x][i].to]); get_siz(center,0); divide(center); } } } }D; int main() { int n,m,u,v,w,i; cin>>n>>m>>L>>R; for(i=1;i<=m;i++) { cin>>c[i]; } for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } for(i=1;i<=n;i++) { sort(e[i].begin(),e[i].end(),cmp); } D.init(n); cout<<ans<<endl; return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18409479,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。