高一上十一月上旬日记
11.1
闲话
- 补 \(whk\) 。补完了英语。
- 下午大课间时 \(miaomiao\) 突然跟我们说让回班拿冲锋衣,今年就不发冬季校服了。
做题纪要
11.2
闲话
- 上午补 \(whk\) 。补完了语文和数学。
- 临吃午饭时突然被 \(miaomiao\) 通知吃饭时间又改成了 \(12:10\) 。
- 详见 2024 NOIP 游记 11.2 。
- 下午 \(miaomiao\) 又说吃饭时间调整了,早饭时间为 \(7:00\) ,午饭时间为 \(12:15\) ,晚饭时间为 \(18:15\) ,与隔壁高二的保持一致;让我们补一下没写完的 \(whk\) 作业,然后写 搜索、模拟 。
- 晚上的全体奥赛生大会 \(miaomiao\) 没让我们去开,需要领奖的同学(指去年 \(NOIP\) 一等的 @K8He 和 @jijidawang 和 @wkh2008 )他找人代替领奖了。
做题纪要
CF372A Counting Kangaroos is Fun
-
再次猜测答案上界在 \(O(\frac{n}{2})\) 左右,双指针扫一遍即可。
点击查看代码
int a[500010]; int main() { int n,ans=0,l,r,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n); for(l=n/2,r=n;l>=1;l--) { if(a[r]>=a[l]*2) { ans++; r--; } } cout<<n-ans<<endl; return 0; }
luogu P11242 碧树
-
画图手摸下样例即可。
点击查看代码
int a[100010]; int main() { int n,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n); for(i=1;i<=n;i++) { if(a[i]==a[n]) { cout<<a[n]-1+(i-1)+(n-i+1)<<endl; break; } } return 0; }
luogu P5305 [GXOI/GZOI2019] 旧词
luogu P3084 [USACO13OPEN] Photo G
-
差分约束。需要 \(SLF\) 优化 \(SPFA\) 加卡时即可。
-
不保证正确性,被叉了属于正常现象。
点击查看代码
struct node { int nxt,to,w; }e[600010]; int head[600010],vis[600010],dis[600010],num[600010],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; } void spfa(int s,int n) { memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); deque<int>q; dis[s]=0; vis[s]=1; q.push_back(s); int tot=0; while(q.empty()==0) { int x=q.front(); vis[x]=0; q.pop_front(); for(int i=head[x];i!=0;i=e[i].nxt) { if(dis[e[i].to]>dis[x]+e[i].w) { dis[e[i].to]=dis[x]+e[i].w; num[e[i].to]=num[x]+1; tot++; if(num[e[i].to]>=n+1||tot>=50000000) { cout<<"-1"<<endl; exit(0); } if(vis[e[i].to]==0) { if(q.empty()==0&&dis[e[i].to]>=dis[q.front()]) { q.push_back(e[i].to); } else { q.push_front(e[i].to); } vis[e[i].to]=1; } } } } } int main() { int n,m,l,r,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>l>>r; add(l-1,r,1); add(r,l-1,-1); } for(i=1;i<=n;i++) { add(i-1,i,1); add(i,i-1,0); } spfa(0,n+1); cout<<dis[n]<<endl; return 0; }
11.3
闲话
- 早上有体活,让 \(7:20\) 到机房。
- 上午 \(7:20 \sim 11:20\) 打学校 \(OJ\) 的模拟赛。
- 打完模拟赛后 \(field\) 突然跟我们说准备把我们都合并到一个机房去,让我们把机房里的 \(whk\) 资料都拿到厕所旁的拐角杂物间去,到杂物间后发现里面堆着“大头电脑”、洗手池和一堆乱七八搜的东西,仅剩空间且没多少了。午饭 \(field\) 让 \(12:15\) 去吃饭。
- 下午放 @lxyt_415x 的每日一歌《经过》。讲题时看了眼隔壁机房,在把 @5k_sync_closer 等人分走后还是不够我们坐的地方,回来后问 \(field\) 说什么时候合并机房,他说就最多两天以内,让我们做好心理准备。
- 临吃晚饭时铃声还是 \(18:15\) 打铃,然后找 \(field\) 说把晚饭时间改了,然后又将铃声改成了正常上课的铃声。
- 晚上让 @lxyt_415x 找 \(field\) 申请激情演唱,然后 \(field\) 就同意了,接着我们就偷偷到机房门口偷听了, \(field\) 还称“唱得还挺好听”。
- 详见 2024 CSP-S 游记 11.3 。
做题纪要
HZTG2082. 最短路
HZTG2080. 玩游戏
11.4
闲话
- 侯操时被通知操前班呼由 \(HZ\) 精神变成了“六无六不”的前三句。
- 早读时 \(feifei\) 过来问我们为啥来了不早读,然后让我们“激情早读”。
- 吃完早饭回机房后 \(feifei\) 跟隔壁说今天没有模拟赛,写 字符串 。我们从门外听见后开始“欢呼雀跃”, \(miaomiao\) 随即问我们要干什么,说今天有模拟赛,他正在组。但我们跟他说昨天题还没改完,然后 \(miaomiao\) \(D\) 了下我们改题效率慢(指需要两天改一场模拟赛),然后就取消了今天的模拟赛。
- 从学校 \(OJ\) 上得知我们打的学校 \(OJ\) 模拟赛叫“加赛”。
- 详见 2024 CSP-S 游记 11.4 。
- 临吃午饭时 \(feifei\) 跟我们说让去吃午饭的时候把电脑直接关机,下午来了后直接按照新座位表就坐,但我们没找到新座位表。
- 下午到机房后发现座位表直接贴在了两个机房的黑板上,原合并至一个机房的计划改成了分成两个机房(据说是按照模拟赛成绩分 \(A,B\) 层)。然后放 @hh弟中弟 的每日一歌《爱的飞行日记》,由 @hh弟中弟 和 @DanhengYinyue 激情演唱,演唱时四个教练都在听,唱的时候 \(huge\) 让我们也像听演唱会一样跟着唱。然后 \(miaomiao,feifei\) 收集了下坏掉的键盘和鼠标。
- 现在左边是 @lxyt_415x ,右边是 @luobotianle 。
- 晚上 @lxyt_415x 哼歌被 \(feifei\) \(D\) 了。
做题纪要
luogu P4899 [IOI2018] werewolf 狼人
-
翻译过来就是询问是否存在一条从 \(s\) 到 \(e\) 的路径使得先只经过编号 \(\ge l\) 的点,再只经过编号 \(\le r\) 的点。
-
考虑建两棵 \(Kruskal\) 重构树。第一棵以每条边端点编号的较小值为边权建立重构树(最大生成树),第二棵以每条边端点编号的较大值为边权建立重构树(最小生成树)。
-
新建出的虚点的点权自然要用边权代替。然后就可以在第一棵重构树上倍增找到点权 \(\ge l\) 的最浅的节点,在第二棵重构树上倍增找到点权 \(\le r\) 的最浅的节点。这两个节点子树内部所有原图中的点就是可行路径上的点。
-
接着需要判断是否有可行的中转点同时在两棵子树内部,主席树维护二维数点判交集即可。
-
具体地,以第一棵子树的 \(DFS\) 序为下标,以第二棵子树的 \(DFS\) 序为权值插入主席树即可。
点击查看代码
int p[400010]; pair<int,int>E[400010]; bool cmp1(pair<int,int> a,pair<int,int>b) { return min(a.first,a.second)>min(b.first,b.second); } bool cmp2(pair<int,int> a,pair<int,int>b) { return max(a.first,a.second)<max(b.first,b.second); } struct DSU { int fa[400010]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } }D; struct Gragh { struct node { int nxt,to; }e[800010]; int head[400010],fa[400010][25],dfn[400010],out[400010],c[400010],cnt=0,tim=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int father) { tim++; dfn[x]=tim; fa[x][0]=father; for(int i=1;i<=20;i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs(e[i].to,x); } } out[x]=tim; } void kruskal(int n,int m,int pd) { D.init(2*n); if(pd==1) { sort(E+1,E+1+m,cmp1); } else { sort(E+1,E+1+m,cmp2); } for(int i=1,tot=n;i<=m&&tot<=2*n-1;i++) { int x=D.find(E[i].first),y=D.find(E[i].second); if(x!=y) { tot++; if(pd==1) { c[tot]=min(E[i].first,E[i].second); } else { c[tot]=max(E[i].first,E[i].second); } D.fa[x]=D.fa[y]=tot; add(tot,x); add(tot,y); } } } int ask(int x,int val,int pd) { int rt=x; if(pd==1) { for(int i=20;i>=0;i--) { if(fa[rt][i]!=0&&c[fa[rt][i]]>=val) { rt=fa[rt][i]; } } } else { for(int i=20;i>=0;i--) { if(fa[rt][i]!=0&&c[fa[rt][i]]<=val) { rt=fa[rt][i]; } } } return rt; } }A,B; struct PDS_SMT { int root[400010],rt_sum; struct SegmentTree { int ls,rs,sum; }tree[400010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int pre,int &rt,int l,int r,int pos,int val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum=tree[pre].sum+val; if(l==r) { return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos,val); } else { update(rson(pre),rson(rt),mid+1,r,pos,val); } } int query(int rt1,int rt2,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt2].sum-tree[rt1].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt1),lson(rt2),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y); } return ans; } }T; bool cmp3(int a,int b) { return A.dfn[a]<A.dfn[b]; } int main() { int n,m,q,s,e,l,r,x,y,i; cin>>n>>m>>q; for(i=1;i<=m;i++) { cin>>E[i].first>>E[i].second; E[i].first++; E[i].second++; } A.kruskal(n,m,1); B.kruskal(n,m,2); A.dfs(2*n-1,0); B.dfs(2*n-1,0); for(i=1;i<=2*n-1;i++) { p[i]=i; } sort(p+1,p+1+2*n-1,cmp3); for(i=1;i<=2*n-1;i++) { T.update(T.root[i-1],T.root[i],1,2*n-1,B.dfn[p[i]],p[i]<=n); } for(i=1;i<=q;i++) { cin>>s>>e>>l>>r; s++; e++; l++; r++; x=A.ask(s,l,1); y=B.ask(e,r,2); cout<<(T.query(T.root[A.dfn[x]-1],T.root[A.out[x]],1,2*n-1,B.dfn[y],B.out[y])!=0)<<endl; } return 0; }
LibreOJ 2865. 「IOI2018」狼人
-
改成交互即可。
-
返回
int
类型的数组和获取 \(m,q\) 是难处理的,不妨用vector
代替。- 但好像 UOJ 407. 【IOI2018】狼人 上直接告诉约定和限制了。
点击查看代码
#include"werewolf.h" int p[400010]; pair<int,int>E[400010]; vector<int>ans; bool cmp1(pair<int,int> a,pair<int,int>b) { return min(a.first,a.second)>min(b.first,b.second); } bool cmp2(pair<int,int> a,pair<int,int>b) { return max(a.first,a.second)<max(b.first,b.second); } struct DSU { int fa[400010]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } }D; struct Gragh { struct node { int nxt,to; }e[800010]; int head[400010],fa[400010][25],dfn[400010],out[400010],c[400010],cnt=0,tim=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x,int father) { tim++; dfn[x]=tim; fa[x][0]=father; for(int i=1;i<=20;i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs(e[i].to,x); } } out[x]=tim; } void kruskal(int n,int m,int pd) { D.init(2*n); if(pd==1) { sort(E+1,E+1+m,cmp1); } else { sort(E+1,E+1+m,cmp2); } for(int i=1,tot=n;i<=m&&tot<=2*n-1;i++) { int x=D.find(E[i].first),y=D.find(E[i].second); if(x!=y) { tot++; if(pd==1) { c[tot]=min(E[i].first,E[i].second); } else { c[tot]=max(E[i].first,E[i].second); } D.fa[x]=D.fa[y]=tot; add(tot,x); add(tot,y); } } } int ask(int x,int val,int pd) { int rt=x; if(pd==1) { for(int i=20;i>=0;i--) { if(fa[rt][i]!=0&&c[fa[rt][i]]>=val) { rt=fa[rt][i]; } } } else { for(int i=20;i>=0;i--) { if(fa[rt][i]!=0&&c[fa[rt][i]]<=val) { rt=fa[rt][i]; } } } return rt; } }A,B; struct PDS_SMT { int root[400010],rt_sum; struct SegmentTree { int ls,rs,sum; }tree[400010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int pre,int &rt,int l,int r,int pos,int val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum=tree[pre].sum+val; if(l==r) { return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos,val); } else { update(rson(pre),rson(rt),mid+1,r,pos,val); } } int query(int rt1,int rt2,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt2].sum-tree[rt1].sum; } int mid=(l+r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt1),lson(rt2),l,mid,x,y); } if(y>mid) { ans+=query(rson(rt1),rson(rt2),mid+1,r,x,y); } return ans; } }T; bool cmp3(int a,int b) { return A.dfn[a]<A.dfn[b]; } vector<int> check_validity(int n,vector<int>u,vector<int>v,vector<int>s,vector<int>e,vector<int>l,vector<int>r) { for(int i=0;i<=u.size()-1;i++) { E[i+1]=make_pair(u[i]+1,v[i]+1); } A.kruskal(n,u.size(),1); B.kruskal(n,u.size(),2); A.dfs(2*n-1,0); B.dfs(2*n-1,0); for(int i=1;i<=2*n-1;i++) { p[i]=i; } sort(p+1,p+1+2*n-1,cmp3); for(int i=1;i<=2*n-1;i++) { T.update(T.root[i-1],T.root[i],1,2*n-1,B.dfn[p[i]],p[i]<=n); } for(int i=0;i<=s.size()-1;i++) { int x=A.ask(s[i]+1,l[i]+1,1); int y=B.ask(e[i]+1,r[i]+1,2); ans.push_back(T.query(T.root[A.dfn[x]-1],T.root[A.out[x]],1,2*n-1,B.dfn[y],B.out[y])!=0); } return ans; }
HZTG2081. 排列
luogu P2491 [SDOI2011] 消防
HZTG2083. 矩形
11.5
闲话
- 早读 \(huge\) 让站到 \(6:40\) 再坐下。
- 上午 \(7:25 \sim 11:25\) 打 accoders NOI 的模拟赛。
- 中午下课时众人正打算坐电梯下去结果电梯到了后 \(miaomiao\) 从里面出来了,我们遂跑到了四楼坐电梯。
- 下午因为高二的信息课和体育课连上了,遂没放每日一歌。讲题的时候 \(feifei\) 突然说我们如果有不一样的解法私下讨论就行,但我们之前都是直接开麦一起讲的。
- 详见 2024 CSP-S 游记 11.5 。
- 临吃晚饭时被 \(feifei\) 通知让我和 @lhx 换下位置,可能是因为我和 @lxyt_415x 魔怔太多被他发现了(?)。现在左边是 @Pursuing_OIer ,右边是 @HANGRY_sol 。
- 晚上 \(huge\) 来了后说了下高二的宿舍内务通报和违纪情况,说这次他救不了违纪的人,而且自从他上次和年级部闹翻后好像年级部查得更严了(指都开始查样被了); \(huge\) 还用 \(ChatGPT\) 新给了他们一个口号,说我们大部分可能文采不太好,上次 @zhengchenxi414 写的宣誓词还不错但不能光让一个人写,让我们好好利用自己手头的资源、工具,加强彼此的合作; \(huge\) 说今天题的难度跟 \(CSP-S\) 难度接近,要是把这套题放在考前打就更好了。
- 详见 2024 NOIP 游记 11.5 。
做题纪要
luogu P4655 [CEOI2017] Building Bridges
-
设 \(f_{i}\) 表示将 \(1\) 和 \(i\) 根柱子连接的最小花费,状态转移方程为 \(f_{i}=\min\limits_{j=1}^{i-1}\{ f_{j}+(h_{i}-h_{j})^{2}+\sum\limits_{k=j+1}^{i-1}w_{k} \}\) ,边界为 \(f_{1}=0\) 。
-
令 \(sum_{i}=\sum\limits_{j=1}^{i}w_{j}\) ,并拆掉上式的 \(\min\) 有 \(\begin{cases} x=h_{j} \\ y=f_{j}+h_{j}^{2}-sum_{j} \\ k=2h_{i} \\ b=f_{i}-h_{i}^{2}-sum_{i-1} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 都不是单调递增的。
-
仍考虑维护下凸壳,但难点在于如何插入。
-
平衡树维护动态插入的代码过于繁琐,考虑使用 \(CDQ\) 分治维护。
- 先
cdq(l,mid)
计算 \(f_{i}(i \in [l,mid])\) ,接着按照 \([l,mid]\) 这个区间内的决策点建凸壳(建凸壳前需要先按照 \(x,y\) 为第一、二关键字排序使其单调),并利用这个凸壳更新 \(f_{i}(i \in [mid+1,r])\) 。-
提供一组 \(hack\) 数据。
- 随便随机几组值域较小的数据就出来了。
点击查看 hack 数据
in: 3 1 4 8 4 3 9 ans: 25
-
- 更新时因为决策点已经固定了,就不再需要像普通 \(DP\) 一样动态插入决策点了,遂可以先将 \([mid+1,r]\) 中的先按照对应的 \(k\) 排序再使用单调队列更新,也可以直接二分求解。前后两种写法分别对应 luogu P10979 任务安排 2 和 luogu P5785 [SDOI2012] 任务安排 。
- 先按照 \(k\) 排序的做法可以一开始就排好序 \(CDQ\) 分治过程中按照和 \(mid\) 的大小关系分组。
- 删除 \([l,mid]\) 中的决策点后就可以继续递归处理下一步,即
cdq(mid+1,r)
。
点击查看代码
ll f[200010],h[200010],w[200010],k[200010],sum[200010],p[200010],tmp[200010]; deque<ll>q; bool cmpk(ll a,ll b) { return k[a]<k[b]; } ll x(ll i) { return h[i]; } ll y(ll i) { return f[i]+h[i]*h[i]-sum[i]; } bool cmpx(ll a,ll b) { return (x(a)==x(b))?(y(a)<y(b)):(x(a)<x(b)); } void cdq(ll l,ll r) { if(l==r) { return; } ll mid=(l+r)/2; for(ll i=l,x=l,y=mid+1;i<=r;i++) { if(p[i]<=mid) { tmp[x]=p[i]; x++; } else { tmp[y]=p[i]; y++; } } for(ll i=l;i<=r;i++) { p[i]=tmp[i]; } cdq(l,mid); q.clear(); for(ll i=l;i<=mid;i++) { while(q.size()>=2&&(y(q.back())-y(q[q.size()-2]))*(x(p[i])-x(q.back()))>=(y(p[i])-y(q.back()))*(x(q.back())-x(q[q.size()-2]))) { q.pop_back(); } q.push_back(p[i]); } for(ll i=mid+1;i<=r;i++) { while(q.size()>=2&&(y(q[1])-y(q.front()))<=k[p[i]]*(x(q[1])-x(q.front()))) { q.pop_front(); } f[p[i]]=min(f[p[i]],f[q.front()]+(h[p[i]]-h[q.front()])*(h[p[i]]-h[q.front()])+sum[p[i]-1]-sum[q.front()]); } cdq(mid+1,r); sort(p+l,p+r+1,cmpx); } int main() { ll n,i; cin>>n; for(i=1;i<=n;i++) { cin>>h[i]; k[i]=h[i]*2; p[i]=i; } for(i=1;i<=n;i++) { cin>>w[i]; sum[i]=sum[i-1]+w[i]; } sort(p+1,p+1+n,cmpk); memset(f,0x3f,sizeof(f)); f[1]=0; cdq(1,n); cout<<f[n]<<endl; return 0; }
- 先
P438. 选彩笔(rgb)
P439. 兵蚁排序(sort)
luogu P4116 Qtree3
-
把编号挂在 \(DFS\) 序上,然后对 \(DFS\) 序修改即可。
-
查询时取 \(DFS\) 序最小的即可。
点击查看代码
struct node { int nxt,to; }e[200010]; int head[200010],dfn[200010],siz[200010],dep[200010],fa[200010],son[200010],top[200010],col[200010],pos[200010],cnt=0,tot=0; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; tot++; dfn[x]=tot; pos[tot]=x; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } struct SMT { struct SegmentTree { int minn; }tree[400010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].minn=min(tree[lson(rt)].minn,tree[rson(rt)].minn); } void build(int rt,int l,int r) { if(l==r) { tree[rt].minn=0x3f3f3f3f; return; } int mid=(l+r)/2; build(lson(rt),l,mid); build(rson(rt),mid+1,r); pushup(rt); } void update(int rt,int l,int r,int pos,int val) { if(l==r) { tree[rt].minn=val; return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(rt),l,mid,pos,val); } else { update(rson(rt),mid+1,r,pos,val); } pushup(rt); } int query(int rt,int l,int r,int x,int y) { if(x<=l&&r<=y) { return tree[rt].minn; } int mid=(l+r)/2,ans=0x3f3f3f3f; if(x<=mid) { ans=min(ans,query(lson(rt),l,mid,x,y)); } if(y>mid) { ans=min(ans,query(rson(rt),mid+1,r,x,y)); } return ans; } }T; void update1(int x,int n) { col[x]^=1; T.update(1,1,n,dfn[x],(col[x]==1)?dfn[x]:0x3f3f3f3f); } int query1(int x,int n) { int ans=0x3f3f3f3f; while(top[x]!=1) { ans=min(ans,T.query(1,1,n,dfn[top[x]],dfn[x])); x=fa[top[x]]; } ans=min(ans,T.query(1,1,n,dfn[1],dfn[x])); return (ans==0x3f3f3f3f)?-1:pos[ans]; } int main() { int n,q,u,v,i; cin>>n>>q; for(i=1;i<=n-1;i++) { cin>>u>>v; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,1); T.build(1,1,n); for(i=1;i<=q;i++) { cin>>u>>v; if(u==0) { update1(v,n); } else { cout<<query1(v,n)<<endl; } } return 0; }
P440. 人口局 DBA(dba)
11.6
闲话
- 吃完早饭后 \(miaomiao\) 过来问昨天晚上 \(huge\) 说的宿舍违纪的人去年级部了吗,然后开始尝试“搜刮”零食。
- 上午 \(7:30 \sim 11:30\) 打学校 \(OJ\) 的模拟赛。
- 下午放 @5k_sync_closer 的每日一歌《蜂鸟》,由 @5k_sync_closer 和 @K8He 激情演唱。然后讲题。
- \(huge\) 找人来把前几天拿来的挂钩钉在了墙上让我们挂衣服用, \(feifei\) 称材料有点劣质。
- 临 \(17:40\) 时 \(huge\) 进来问我们怎么讨论时间都在自己做题,做题时间都在各自讨论,这不是有铃声提醒吗。我说我们怎么知道是讨论铃声还是做题铃声,然后 \(huge\) 说这是个好问题,就开始 \(D\) 我们不认真看时间表,记不住时间,然后就扯到了高二那边宿舍违纪和早操情况。
做题纪要
HZTG5733. 新的阶乘
HZTG5734. 博弈树
HZTG5735. 划分
luogu P4360 [CEOI2004] 锯木厂选址
-
设 \(f_{i}\) 表示把第二个锯木厂建立在第 \(i\) 棵树时的最小花费,状态转移方程为 \(f_{i}=\min\limits_{j=1}^{i-1}\{ \sum\limits_{k=1}^{j}\sum\limits_{h=k}^{j}w _{k}d_{h}+\sum\limits_{k=j+1}^{i}\sum\limits_{h=k}^{i}w_{k}d_{h}+\sum\limits_{k=i+1}^{n}\sum\limits_{h=k}^{n}w_{k}d_{h} \}\) 。
-
考虑费用提前计算,设 \(dis_{i}=\sum\limits_{j=i}^{n}d_{j},sum_{i}=\sum\limits_{j=1}^{i}w_{i}\) ,上式等价于 \(f_{i}=\min\limits_{j=1}^{i-1}\{\sum\limits_{k=1}^{n}w_{k}dis_{k}-sum_{j} \times dis_{j}-dis_{i} \times (sum_{i}-sum_{j}) \}=\sum\limits_{k=1}^{n}w_{k}dis_{k}-dis_{i} \times sum_{i}+\max\limits_{j=1}^{i-1}\{sum_{j} \times dis_{j}-dis_{i} \times sum_{j} \}\) 。
-
拆掉 \(\max\) ,有 \(\begin{cases} x=sum_{j} \\ y=sum_{j} \times dis_{j} \\ k=dis_{i} \\ b=f_{i}-\sum\limits_{k=1}^{n}w_{k}dis_{k}+dis_{i} \times sum_{i} \end{cases}\) ,其中横坐标 \(x\) 是单调递增的,斜率 \(k\) 是单调递减的。
-
单调队列维护上凸壳即可。
点击查看代码
ll w[200010],d[200010],sum[200010],dis[200010]; deque<ll>q; ll x(ll i) { return sum[i]; } ll y(ll i) { return sum[i]*dis[i]; } int main() { ll n,num=0,ans=0x7f7f7f7f,i; cin>>n; for(i=1;i<=n;i++) { cin>>w[i]>>d[i]; sum[i]=sum[i-1]+w[i]; } for(i=n;i>=1;i--) { dis[i]=dis[i+1]+d[i]; num+=dis[i]*w[i]; } q.push_back(1); for(i=2;i<=n;i++) { while(q.size()>=2&&(y(q[1])-y(q.front()))>=(x(q[1])-x(q.front()))*dis[i]) { q.pop_front(); } ans=min(ans,num-sum[q.front()]*dis[q.front()]-dis[i]*(sum[i]-sum[q.front()])); while(q.size()>=2&&(y(q.back())-y(q[q.size()-2]))*(x(i)-x(q.back()))<=(y(i)-y(q.back()))*(x(q.back())-x(q[q.size()-2]))) { q.pop_back(); } q.push_back(i); } cout<<ans<<endl; return 0; }
luogu P4072 [SDOI2016] 征途
-
\(s^{2}=\overline{x^{2}}-\overline{x}^{2}\) 比较显然;提前 \(m\) 乘进去避免精度误差;把距离挂在点上,不妨钦定第 \(m\) 天在 \(n\) 地过夜;考虑只统计平方的贡献
-
设 \(f_{i,j}\) 表示第 \(i\) 天在 \(j\) 地过夜的最小代价,状态转移方程为 \(f_{i,j}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+(\sum\limits_{h=k+1}^{j}d_{h})^{2} \}\) 。
-
设 \(sum_{i}=\sum\limits_{j=1}^{i}d_{j}\) ,上式等价于 \(f_{i,j}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+(sum_{j}-sum_{k})^{2} \}=\min\limits_{k=i-1}^{j-1}\{ f_{i-1,k}+sum_{j}^{2}+sum_{k}^{2}-2sum_{j} \times sum_{k} \}\) 。
-
拆掉 \(\min\) ,有 \(\begin{cases} x=sum_{k} \\ y=f_{i-1,k}+sum_{k}^{2} \\ k=2sum_{j} \\ b=f_{i,j}-sum_{j}^{2} \end{cases}\) ,其中横坐标 \(x\) 和斜率 \(k\) 是单调递增的。
-
对于每个 \(i\) 分别单调队列维护下凸壳即可。
-
最终有 \(mf_{m,n}-sum_{n} \times sum_{n}\) 即为所求。
点击查看代码
ll d[3010],sum[3010],f[2][3010]; deque<ll>q; ll x(ll k) { return sum[k]; } ll y(ll i,ll k) { return f[i&1][k]+sum[k]*sum[k]; } int main() { ll n,m,i,j; cin>>n>>m; for(i=1;i<=n;i++) { cin>>d[i]; sum[i]=sum[i-1]+d[i]; } memset(f,0x3f,sizeof(f)); for(i=1;i<=n;i++) { f[1][i]=sum[i]*sum[i]; } for(i=2;i<=m;i++) { q.clear(); q.push_back(i-1); for(j=i;j<=n;j++) { while(q.size()>=2&&(y(i-1,q[1])-y(i-1,q.front()))<=(x(q[1])-x(q.front()))*2*sum[j]) { q.pop_front(); } f[i&1][j]=f[(i-1)&1][q.front()]+(sum[j]-sum[q.front()])*(sum[j]-sum[q.front()]); while(q.size()>=2&&(y(i-1,q.back())-y(i-1,q[q.size()-2]))*(x(j)-x(q.back()))>=(y(i-1,j)-y(i-1,q.back()))*(x(q.back())-x(q[q.size()-2]))) { q.pop_back(); } q.push_back(j); } } cout<<m*f[m&1][n]-sum[n]*sum[n]<<endl; return 0; }
11.7
闲话
- 上午 \(7:25 \sim 11:55\) 打 accoders NOI 的模拟赛。
- 下午 \(huge\) 强调了下机房讨论、宿舍泡面(自己保管好,别被查到就行)、个人卫生问题; \(huge\) 看见 @CuFeO4 的冲锋衣后说 \(HZ\) 终于给我们做件像样的校服了。
- 吃完晚饭后 @CTHoi 把 \(505\) 机房外门把手弄折了(我之前开就感觉门把手有点松了,没想到真能弄下来), \(miaomiao\) 来了后把涉及到的人员交出去处理了一下,然后就回来 \(D\) 我们这两届是他见过的习惯最差的, \(D\) 我们 \(CSP\) 考得分太低了,高一的还好,高二的不好好干事还成天捣蛋、故意把东西用坏。上次 9.20 时他好不容易给个高一的请下假来复习初赛,然后就因为按电梯这事就把高一的都赶回去了,要是他是高二这届的主教练,他早就让他们走了。
- 晚上听多校的讲题。
做题纪要
P470. 图书管理
P472. 函数
P471. 两棵树
luogu P2480 [SDOI2010] 古代猪文
-
等价于求 \(g^{\sum\limits_{d|n}\binom{n}{\frac{n}{d}}} \bmod 999911659=g^{\sum\limits_{d|n}\binom{n}{d}} \bmod 999911659\) 。
-
观察到指数较大且 \(999911659\) 是质数,考虑欧拉定理。
-
当 \(g \ne 999911659\) 时等价于求 \(g^{\sum\limits_{d|n}\binom{n}{d} \bmod 999911658} \bmod 999911659\) 。
-
直接跑 \(exLucas\) 显然可以做,但考虑更加简便的做法。
-
对 \(999911658\) 进行质因数分解后可以得到 \(999911658=2 \times 3 \times 4679 \times 35617\) ,分别 \(Lucas\) 计算后用 \(CRT\) 合并即可。
点击查看代码
const ll p=999911659,m[5]={0,2,3,4679,35617}; ll a[5]; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } ll C(ll n,ll m,ll p) { if(n>=m&&n>=0&&m>=0) { ll up=1,down=1; for(ll i=n-m+1;i<=n;i++) { up=up*i%p; } for(ll i=1;i<=m;i++) { down=down*i%p; } return up*qpow(down,p-2,p)%p; } else { return 0; } } ll lucas(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?(m?C(n%p,m%p,p)*lucas(n/p,m/p,p)%p:1):0; } void exgcd(ll a,ll b,ll &x,ll &y) { if(b==0) { x=1; y=0; } else { exgcd(b,a%b,y,x); y-=a/b*x; } } ll inv(ll a,ll p) { ll x=0,y=0; exgcd(a,p,x,y); return (x%p+p)%p; } ll crt(ll n) { ll mul=1,r,ans=0; for(ll i=1;i<=n;i++) { mul*=m[i]; } for(ll i=1;i<=n;i++) { r=mul/m[i]; ans=(ans+(a[i]*r%mul)*inv(r,m[i])%mul)%mul; } return ans; } int main() { ll n,g,i,d; cin>>n>>g; if(g%p==0) { cout<<0<<endl; } else { for(i=1;i<=4;i++) { for(d=1;d<=sqrt(n);d++) { if(n%d==0) { if(d*d==n) { a[i]=(a[i]+lucas(n,d,m[i]))%m[i]; } else { a[i]=(a[i]+lucas(n,d,m[i]))%m[i]; a[i]=(a[i]+lucas(n,n/d,m[i]))%m[i]; } } } } cout<<qpow(g,crt(4),p)<<endl; } return 0; }
luogu P7984 [USACO21DEC] Tickets P
-
考虑对票建虚点,从 \(c_{i}\) 向 \(i+n\) 连一条权值为 \(p_{i}\) 的边,然后从 \(i+n\) 向 \([a_{i},b_{i}]\) 连一条权值为 \(0\) 的边。
-
建出反图后 \(1 \to i\) 和 \(n \to i\) 的路径集合会有重复统计的部分,不妨以 \(dis_{1,i}+dis_{n,i}\) 作为初始值然后再进行一遍松弛操作(若没有重复部分就不需要松弛了)。
-
然后就是线段树优化建图板子了。
点击查看代码
struct SMT_Q_BG { ll id[900010],dis[2][900010],d[900010],vis[900010]; vector<pair<ll,ll> >e[900010]; ll lson(ll x) { return x*2; } ll rson(ll x) { return x*2+1; } void build(ll rt,ll l,ll r,ll n) { e[rt+n*4].push_back(make_pair(rt,0)); if(l==r) { id[l]=rt; return; } e[lson(rt)].push_back(make_pair(rt,0)); e[rson(rt)].push_back(make_pair(rt,0)); e[rt+n*4].push_back(make_pair(lson(rt)+n*4,0)); e[rt+n*4].push_back(make_pair(rson(rt)+n*4,0)); ll mid=(l+r)/2; build(lson(rt),l,mid,n); build(rson(rt),mid+1,r,n); } void update(ll rt,ll l,ll r,ll x,ll y,ll pos,ll w,ll n) { if(x<=l&&r<=y) { e[rt].push_back(make_pair(pos+n*8,w)); return; } ll mid=(l+r)/2; if(x<=mid) { update(lson(rt),l,mid,x,y,pos,w,n); } if(y>mid) { update(rson(rt),mid+1,r,x,y,pos,w,n); } } void dijkstra1(ll s,ll id) { memset(vis,0,sizeof(vis)); memset(dis[id],0x3f,sizeof(dis[id])); priority_queue<pair<ll,ll> >q; dis[id][s]=0; q.push(make_pair(-dis[id][s],s)); while(q.empty()==0) { ll x=q.top().second; q.pop(); if(vis[x]==0) { vis[x]=1; for(ll i=0;i<e[x].size();i++) { if(dis[id][e[x][i].first]>dis[id][x]+e[x][i].second) { dis[id][e[x][i].first]=dis[id][x]+e[x][i].second; q.push(make_pair(-dis[id][e[x][i].first],e[x][i].first)); } } } } } void dijkstra2(ll n,ll k) { memset(vis,0,sizeof(vis)); priority_queue<pair<ll,ll> >q; for(ll i=1;i<=8*n+k;i++) { d[i]=dis[0][i]+dis[1][i]; q.push(make_pair(-d[i],i)); } while(q.empty()==0) { ll x=q.top().second; q.pop(); if(vis[x]==0) { vis[x]=1; for(ll i=0;i<e[x].size();i++) { if(d[e[x][i].first]>d[x]+e[x][i].second) { d[e[x][i].first]=d[x]+e[x][i].second; q.push(make_pair(-d[e[x][i].first],e[x][i].first)); } } } } } }T; int main() { ll n,k,c,p,a,b,i; cin>>n>>k; T.build(1,1,n,n); for(i=1;i<=k;i++) { cin>>c>>p>>a>>b; T.e[i+n*8].push_back(make_pair(T.id[c]+n*4,p)); T.update(1,1,n,a,b,i,0,n); } T.dijkstra1(T.id[1],0); T.dijkstra1(T.id[n],1); T.dijkstra2(n,k); for(i=1;i<=n;i++) { cout<<(T.d[T.id[i]]<1e15?T.d[T.id[i]]:-1)<<endl; } return 0; }
11.8
闲话
- 侯操时级部让进行期中考试的目标宣誓,但因为上次我们就没参加(某个早预备),遂不知道誓词是啥。
做题纪要
luogu P4408 [NOI2003] 逃学的小孩
-
题目中保证了 \(m=n-1\) ,直接套用直径端点的结论即可。
点击查看代码
struct node { ll nxt,to,w; }e[200010]; ll head[200010],dep[200010],dis[200010],fa[200010],siz[200010],son[200010],top[200010],cnt=0,tot=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; } void dfs(ll x,ll father,ll w) { dis[x]=dis[father]+w; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs(e[i].to,x,e[i].w); } } } void dfs1(ll x,ll father,ll w) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; dis[x]=dis[father]+w; for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x,e[i].w); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(ll x,ll id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(ll i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa[x]&&e[i].to!=son[x]) { dfs2(e[i].to,e[i].to); } } } } ll lca(ll u,ll v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { u=fa[top[u]]; } else { v=fa[top[v]]; } } return dep[u]<dep[v]?u:v; } ll get_dis(ll u,ll v) { return dis[u]+dis[v]-2*dis[lca(u,v)]; } int main() { ll n,m,u,v,w,ans=0,rt1=0,rt2=0,d,i; cin>>n>>m; for(i=1;i<=n-1;i++) { cin>>u>>v>>w; add(u,v,w); add(v,u,w); } dfs(1,0,0); for(i=1;i<=n;i++) { rt1=(dis[i]>dis[rt1])?i:rt1; } dfs(rt1,0,0); for(i=1;i<=n;i++) { rt2=(dis[i]>dis[rt2])?i:rt2; } d=dis[rt2]; dfs1(1,0,0); dfs2(1,1); for(i=1;i<=n;i++) { ans=max(ans,min(get_dis(i,rt1),get_dis(i,rt2))+d); } cout<<ans<<endl; return 0; }
QOJ 5312.Levenshtein Distance
luogu P4768 [NOI2018] 归程
luogu P4197 Peaks
-
建立 \(Kruskal\) 重构树(最小生成树),主席树维护子树内第 \(k\) 大即可。
点击查看代码
struct node { int from,to,w; }; int h[200010],dfn[200010],out[200010],pos[200010],c[200010],fa[200010][25],tot=0; vector<node>E; vector<int>e[200010]; void add(int u,int v) { e[u].push_back(v); } bool cmp(node a,node b) { return a.w<b.w; } struct DSU { int fa[200010]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } }D; void kruskal(int n) { D.init(2*n-1); sort(E.begin(),E.end(),cmp); for(int i=0,tot=n;i<E.size()&&tot<=2*n-1;i++) { int x=D.find(E[i].from),y=D.find(E[i].to); if(x!=y) { tot++; c[tot]=E[i].w; D.fa[x]=D.fa[y]=tot; add(tot,x); add(tot,y); } } } void dfs(int x,int father) { tot++; dfn[x]=tot; pos[tot]=x; fa[x][0]=father; for(int i=1;i<=20;i++) { fa[x][i]=fa[fa[x][i-1]][i-1]; } for(int i=0;i<e[x].size();i++) { if(e[x][i]!=father) { dfs(e[x][i],x); } } out[x]=tot; } int ask(int x,int val) { int rt=x; for(int i=20;i>=0;i--) { if(fa[rt][i]!=0&&c[fa[rt][i]]<=val) { rt=fa[rt][i]; } } return rt; } struct PDS_SMT { int root[200010],rt_sum; struct SegmentTree { int ls,rs,sum; }tree[200010<<5]; #define lson(rt) (tree[rt].ls) #define rson(rt) (tree[rt].rs) int build_rt() { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].sum=0; return rt_sum; } void update(int pre,int &rt,int l,int r,int pos,int val) { rt=build_rt(); tree[rt]=tree[pre]; tree[rt].sum+=val; if(l==r) { return; } int mid=(l+r)/2; if(pos<=mid) { update(lson(pre),lson(rt),l,mid,pos,val); } else { update(rson(pre),rson(rt),mid+1,r,pos,val); } } int query(int rt1,int rt2,int l,int r,int k) { if(l==r) { return (tree[rt2].sum-tree[rt1].sum>=k)?l:-1; } int mid=(l+r)/2; if(tree[rson(rt2)].sum-tree[rson(rt1)].sum>=k) { return query(rson(rt1),rson(rt2),mid+1,r,k); } else { return query(lson(rt1),lson(rt2),l,mid,k-(tree[rson(rt2)].sum-tree[rson(rt1)].sum)); } } }T; int main() { int n,m,q,u,v,w,i; cin>>n>>m>>q; for(i=1;i<=n;i++) { cin>>h[i]; } for(i=1;i<=m;i++) { cin>>u>>v>>w; E.push_back((node){u,v,w}); } kruskal(n); dfs(2*n-1,0); for(i=1;i<=2*n-1;i++) { T.root[i]=T.root[i-1]; if(pos[i]<=n) { T.update(T.root[i],T.root[i],1,1000000000,h[pos[i]],1); } } for(i=1;i<=q;i++) { cin>>u>>v>>w; u=ask(u,v); cout<<T.query(T.root[dfn[u]-1],T.root[out[u]],1,1000000000,w)<<endl; } return 0; }
UVA1395 苗条的生成树 Slim Span
-
强化版: luogu P4234 最小差值生成树
点击查看代码
struct node { int from,to,w; }e[10010]; bool cmp(node a,node b) { return a.w<b.w; } struct DSU { int fa[510]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } int find(int x) { return (fa[x]==x)?x:fa[x]=find(fa[x]); } }D; int main() { int n,m,x,y,cnt,ans,i,j; while(cin>>n>>m) { if(n==0&&m==0) { break; } else { ans=0x3f3f3f3f; for(i=1;i<=m;i++) { cin>>e[i].from>>e[i].to>>e[i].w; } sort(e+1,e+1+m,cmp); for(i=1;i<=m;i++) { D.init(n); cnt=0; for(j=i;j<=m;j++) { x=D.find(e[j].from); y=D.find(e[j].to); if(x!=y) { D.fa[x]=y; cnt++; if(cnt==n-1) { ans=min(ans,e[j].w-e[i].w); break; } } } } cout<<((ans==0x3f3f3f3f)?-1:ans)<<endl; } } return 0; }
P441. 银行的源起(banking)
luogu P1709 [USACO5.5] 隐藏口令 Hidden Password
-
最小表示法板子。
点击查看代码
char s[10000010]; int main() { int n,i,l,r,len; cin>>n; for(i=1;i<=n;i++) { cin>>s[i]; s[n+i]=s[i]; } l=1; r=2; while(l<=n&&r<=n) { len=0; while(len<=n-1&&s[l+len]==s[r+len]) { len++; } if(len==n) { break; } else { if(s[l+len]>s[r+len]) { l+=len+1; l+=(l==r); } else { r+=len+1; r+=(l==r); } } } cout<<min(l,r)-1<<endl; return 0; }
luogu P11253 [GDKOI2023 普及组] 小学生数学
-
\(f(i)=\frac{1}{i^{k}} \bmod p\) 是完全积性函数。
点击查看代码
const ll p=998244353; ll prime[3000010],inv[20000010],len=0; bool vis[20000010]; ll qpow(ll a,ll b) { ll ans=1; while(b) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void isprime(ll n,ll k) { memset(vis,0,sizeof(vis)); inv[1]=1; for(ll i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; inv[i]=qpow(qpow(i,k),p-2); } for(ll j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; inv[i*prime[j]]=inv[i]*inv[prime[j]]%p; if(i%prime[j]==0) { break; } } } } int main() { ll n,k,ans=0,mul=1,i; cin>>n>>k; isprime(n,k); for(i=1;i<=n;i++) { mul=mul*i%p; ans=(ans+mul*inv[i]%p)%p; } cout<<ans<<endl; return 0; }
11.9
闲话
- 早操时看见来参观的人了,貌似今天 \(HZ\) 又有教研活动。
- 上午 \(7:20 \sim 11:50\) 打 accoders NOI 的模拟赛。
- 因为模拟赛时间和高二体活时间冲突了,所以让我们中午 \(11:55\) 吃饭,下午一起补体活。
- 下午听多校的讲题; \(feifei\) 先是被头顶的钟表掉下来砸着了腰,后来起身时又被耳机线缠住了腿,称“不要说话了啊,我今天很暴躁”。
- 感觉 \(feifei\) 有班主任那味了。
做题纪要
P481. 星际联邦
luogu P10743 [SEERC2020] AND = OR
luogu P9488 ZHY 的生成树
-
\((u,v)\) 两点间的距离为 \(\gcd(u,v) \in [1,\min(u,v)]\) 。
-
最小生成树显然是取到下界时的情况,最大生成树自然取到上界即可。
-
具体地,从 \(u\) 向 \(u\) 的质数倍的点连边。需要手动模拟 \(Kruskal\) 排序的过程。
点击查看代码
int prime[700000],len=0; bool vis[10000010]; void isprime(int n) { memset(vis,0,sizeof(vis)); for(int i=2;i<=n;i++) { if(vis[i]==0) { len++; prime[len]=i; } for(int j=1;j<=len&&i*prime[j]<=n;j++) { vis[i*prime[j]]=1; if(i%prime[j]==0) { break; } } } } struct DSU { int fa[10000010]; void init(int n) { for(int i=1;i<=n;i++) { fa[i]=i; } } int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); } }D; ll kruskal(int n) { ll ans=0; D.init(n); for(int i=n/2;i>=1;i--) { for(int j=1;j<=len&&i*prime[j]<=n;j++) { int x=D.find(i),y=D.find(i*prime[j]); if(x!=y) { ans+=i; D.fa[x]=y; } } } return ans; } int main() { int n; cin>>n; isprime(n); cout<<kruskal(n)<<endl; return 0; }
P483. 摆烂合唱
11.10
闲话
- 因为下周二要考期中,所以今天没有体活,让 \(6:30\) 到位。
- 上午 \(7:30 \sim 12:00\) 打学校 \(OJ\) 的模拟赛。
- 下午讲题。
做题纪要
CF2033B Sakurako and Water
CF2025B Binomial Coefficients, Kind Of
CF2030D QED's Favorite Permutation
luogu P11271 「Diligent-OI R1 A」DlgtPattern
-
四个方向分讨。
点击查看代码
int main() { ll x,y,p,q; cin>>x>>y>>p>>q; cout<<min({p,q,x-p,y-q})<<endl; return 0; }
luogu P11272 「Diligent-OI R1 B」DlgtArray
-
分讨 \(0,1\) 的个数。
点击查看代码
int a[1000010],sum[1000010]; int main() { int n,q,l,r,k,i; scanf("%d%d",&n,&q); for(i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(i=1;i<=q;i++) { scanf("%d%d%d",&l,&r,&k); if(l==r||k>=r-l+1) { if(k==0) { cout<<0<<endl; } else { cout<<-1<<endl; } } else { if(k==r-l) { if(sum[r]-sum[l-1]==r-l+1) { printf("%d\n",0); } else { printf("%d\n",k-(sum[r]-sum[l-1])); } } else { printf("%d\n",abs(sum[r]-sum[l-1]-k)); } } } return 0; }
luogu P11273 「Diligent-OI R1 C」DlgtRank
-
倒着考虑即可。
点击查看代码
ll a[200010],b[200010],ans[200010]; int main() { ll n,k,i; cin>>n>>k; for(i=1;i<=n;i++) { cin>>a[i]; b[i]=a[i]; } sort(b+1,b+1+n); b[0]=unique(b+1,b+1+n)-(b+1); for(i=b[0]-1;i>=1;i--) { ans[i]=max(0ll,ans[i+1]-(b[i+1]-b[i]-1)/k+1); } for(i=1;i<=n;i++) { cout<<ans[lower_bound(b+1,b+1+b[0],a[i])-b]<<" "; } return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18521952,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。