九下五月下旬日记
5.21
闲话
- 早操忘换操前读了,还拿着理化实验的题签来读,幸好现班主任早操没来查。
- 政治课做了三轮复习的宣讲。
- 数学课上,称下节课开三轮复习。
- 物理课上,现物理老师再次对我们回班后能否适应感到担忧。
- \(miaomiao\) 把 @yswn , @APJifengc 和 @IIIIIlIIIl 带回来了。@yswn 进来后还抓拍了一张机房照片。接着因后排机位“不足”爆发了一场“占机位大赛”。
- 晚三现语文老师突然问我们这节课是否有安排,在我们回答没有后让我们帮她批改 \(23,24\) 班的语文卷子。
做题纪要
UVA12345 Dynamic len(set(a[L:R]))
-
带修莫队板子。
点击查看代码
int a[1000010],cnt[1000010],L[1000010],R[1000010],pos[1000010],ans[1000010],klen,ksum; struct ask { int l,r,id,tim; }q[1000010]; struct change { int pos,col; }c[1000010]; bool q_cmp(ask a,ask b) { return (pos[a.l]==pos[b.l])?((pos[a.r]==pos[b.r])?(a.tim<b.tim):(a.r<b.r)):(a.l<b.l); } void init(int n) { klen=pow(n,2.0/3); ksum=n/klen; for(int i=1;i<=ksum;i++) { L[i]=R[i-1]+1; R[i]=R[i-1]+klen; } if(R[ksum]<n) { ksum++; L[ksum]=R[ksum-1]+1; R[ksum]=n; } for(int i=1;i<=ksum;i++) { for(int j=L[i];j<=R[i];j++) { pos[j]=i; } } } void add(int x,int &sum) { cnt[x]++; sum+=(cnt[x]==1); } void del(int x,int &sum) { cnt[x]--; sum-=(cnt[x]==0); } int main() { int n,m,l=1,r=0,tim=0,sum=0,qcnt=0,ccnt=0,i; char pd; cin>>n>>m; for(i=1;i<=n;i++) { cin>>a[i]; } init(n); for(i=1;i<=m;i++) { cin>>pd; if(pd=='Q') { qcnt++; cin>>q[qcnt].l>>q[qcnt].r; q[qcnt].l++; q[qcnt].id=qcnt; q[qcnt].tim=ccnt; } else { ccnt++; cin>>c[ccnt].pos>>c[ccnt].col; c[ccnt].pos++; } } sort(q+1,q+1+qcnt,q_cmp); for(i=1;i<=qcnt;i++) { while(l>q[i].l) { l--; add(a[l],sum); } while(r<q[i].r) { r++; add(a[r],sum); } while(l<q[i].l) { del(a[l],sum); l++; } while(r>q[i].r) { del(a[r],sum); r--; } while(tim<q[i].tim) { tim++; if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); } while(tim>q[i].tim) { if(l<=c[tim].pos&&c[tim].pos<=r) { del(a[c[tim].pos],sum); add(c[tim].col,sum); } swap(a[c[tim].pos],c[tim].col); tim--; } ans[q[i].id]=sum; } for(i=1;i<=qcnt;i++) { cout<<ans[i]<<endl; } return 0; }
UVA1674 Lightning Energy Report
-
路径修改、单点查询板子。
点击查看代码
struct node { int nxt,to; }e[100010]; int head[100010],c[100010],cc[100010],siz[100010],fa[100010],dep[100010],son[100010],top[100010],dfn[100010],cnt=0,tot=0; struct SMT { struct SegmentTree { int l,r,sum,lazy; }tree[200010]; int lson(int x) { return x*2; } int rson(int x) { return x*2+1; } void pushup(int rt) { tree[rt].sum=tree[lson(rt)].sum+tree[rson(rt)].sum; } 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].sum=cc[l]; 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!=0) { 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[lson(rt)].lazy+=tree[rt].lazy; tree[rson(rt)].lazy+=tree[rt].lazy; tree[rt].lazy=0; } } void update(int rt,int x,int y,int val) { if(x<=tree[rt].l&&tree[rt].r<=y) { tree[rt].sum+=val*(tree[rt].r-tree[rt].l+1); tree[rt].lazy+=val; return; } pushdown(rt); int 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); } } int query(int rt,int x,int y) { if(x<=tree[rt].l&&tree[rt].r<=y) { return tree[rt].sum; } pushdown(rt); int mid=(tree[rt].l+tree[rt].r)/2,ans=0; if(x<=mid) { ans+=query(lson(rt),x,y); } if(y>mid) { ans+=query(rson(rt),x,y); } return ans; } }T; 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 father,int id) { top[x]=id; tot++; dfn[x]=tot; cc[tot]=c[x]; if(son[x]!=0) { dfs2(son[x],x,id); for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father&&e[i].to!=son[x]) { dfs2(e[i].to,x,e[i].to); } } } } void update1(int u,int v,int val) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) { T.update(1,dfn[top[u]],dfn[u],val); u=fa[top[u]]; } else { T.update(1,dfn[top[v]],dfn[v],val); v=fa[top[v]]; } } if(dep[u]<dep[v]) { T.update(1,dfn[u],dfn[v],val); } else { T.update(1,dfn[v],dfn[u],val); } } int query1(int pos) { return T.query(1,dfn[pos],dfn[pos]); } int main() { int t,n,m,u,v,w,i,j; cin>>t; for(j=1;j<=t;j++) { cnt=tot=0; memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); memset(son,0,sizeof(son)); cin>>n; for(i=1;i<=n-1;i++) { cin>>u>>v; u++; v++; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,0,1); T.build(1,1,n); cin>>m; for(i=1;i<=m;i++) { cin>>u>>v>>w; u++; v++; update1(u,v,w); } cout<<"Case #"<<j<<":"<<endl; for(i=1;i<=n;i++) { cout<<query1(i)<<endl; } } return 0; }
UVA10905 Children's Game
-
多倍经验: luogu P1012 [NOIP1998 提高组] 拼数
点击查看代码
string a[100]; bool cmp(string a,string b) { return a+b>b+a; } int main() { int n,i; while(cin>>n) { if(n==0) { break; } else { for(i=1;i<=n;i++) { cin>>a[i]; } sort(a+1,a+1+n,cmp); for(i=1;i<=n;i++) { cout<<a[i]; } cout<<endl; } } return 0; }
luogu P4309 [TJOI2013] 最长上升子序列
-
由于每次插入的数是递增的,原本的状态转移方程 \(f_{i}=\max\limits_{j=1}^{i-1} \{ [j<i] \times [a_{j}<a_{i}] \times f_{j} \}+1\) 可以简化成 \(f_{i}=\max\limits_{j=1}^{i-1} \{ [j<i] \times f_{j} \}+1\) 。
-
平衡树在动态插入的过程中,维护 \(f\) 即可。
点击查看代码
struct BST { const int INF=0x7f7f7f7f; int rt_sum,root; struct FHQ_Treap { int son[2],val,rnd,cnt,siz,f; }tree[100010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=root=0; srand(time(0)); } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; tree[rt].f=max(tree[rt].val,max(tree[lson(rt)].f,tree[rson(rt)].f)); } int build(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].val=tree[rt_sum].f=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void split(int rt,int k,int &ls,int &rs) { if(rt==0) { ls=rs=0; return; } if(tree[lson(rt)].siz+tree[rt].cnt<=k) { ls=rt; split(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt,rson(ls),rs); } else { rs=rt; split(lson(rs),k,ls,lson(rs)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) { return rt1+rt2; } if(tree[rt1].rnd<tree[rt2].rnd) { rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } int query(int rt) { return tree[rt].f; } void insert(int pos) { int ls,rs; split(root,pos,ls,rs); root=merge(merge(ls,build(query(ls)+1)),rs); } }T; int main() { int n,x,i; cin>>n; for(i=1;i<=n;i++) { cin>>x; T.insert(x); cout<<T.query(T.root)<<endl; } return 0; }
5.22
闲话
- 历史课上现历史老师讲了下信息技术和计算机技术的区别,先问的是班里有没有学信奥的(他是知道班里有的),然后问我们一亿的运算量大概是什么概念,我脱口而出其差不多就是现在普及的计算机 \(1s\) 的运算量(又忘了一亿是 \(1e8\) 了。。。),然后问我们超级计算机在实际生活的用途。
- 不知道为啥把 \(4\) 楼机房门上的玻璃全拆了,还在 \(5\) 楼的门上划好了装玻璃的痕迹(教练是看平常机房太闷了吗?)。
- 去厕所时途径 \(4\) 楼时,看见原班主任在给高一的讲变量初始化相关东西。
- 晚上回宿舍后发现电话线被断了(上次年级部开家长会时说了,但我没当回事),什么傻逼学校,我是真搞不懂断电话线和备战中考有什么关系。
做题纪要
luogu P3165 [CQOI2014] 排序机械臂
-
多倍经验: luogu P4402 [Cerc2007] robotic sort 机械排序 | SP2059 CERC07S - Robotic Sort | UVA1402 Robotic Sort
-
由于每个数的下标不再等于权值,仍将下标插入到文艺平衡树中,同时记录插入的节点使其作为第二关键字参与排序(第一关键字为 \(P\) )。
-
每次操作时,将第二维旋转至根节点,此时左子树的大小即为所求(因为有哨兵)。
-
因为旋转顺序的不同,注意在
splay
的过程中也需要涉及到pushdown
。点击查看代码
pair<int,int>a[100010]; struct BST { const int INF=0x7f7f7f7f; int rt_sum,root; struct Splay { int son[2],fa,val,cnt,siz,lazy; }tree[100010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) #define dson(rt,d) (tree[rt].son[d]) #define ason(rt,d) (tree[rt].son[d^1]) BST() { rt_sum=root=0; insert(INF); insert(-INF); } int fdson(int rt) { return ((rt==lson(tree[rt].fa))?0:1); } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; } int build(int val,int fa) { rt_sum++; lson(rt_sum)=rson(rt_sum)=0; tree[rt_sum].fa=fa; tree[rt_sum].val=val; tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushdown(int rt) { if(rt!=0&&tree[rt].lazy!=0) { swap(lson(rt),rson(rt)); tree[lson(rt)].lazy^=1; tree[rson(rt)].lazy^=1; tree[rt].lazy=0; } } void rotate(int x) { int y=tree[x].fa,z=tree[y].fa,d=fdson(x); dson(z,fdson(y))=x; tree[x].fa=z; dson(y,d)=ason(x,d); tree[ason(x,d)].fa=y; ason(x,d)=y; tree[y].fa=x; pushup(y); pushup(x); } void splay(int x,int to) { while(tree[x].fa!=to) { int y=tree[x].fa,z=tree[y].fa; pushdown(z); pushdown(y); pushdown(x); if(z!=to) { rotate((fdson(x)==fdson(y))?y:x); } rotate(x); } root=(to==0)?x:root; } int insert(int val) { int rt=root,fa=0; while(rt!=0&&tree[rt].val!=val) { fa=rt; rt=dson(rt,val>tree[rt].val); } if(rt==0) { rt=build(val,fa); if(fa!=0) { dson(fa,val>tree[fa].val)=rt; } } else { tree[rt].cnt++; } splay(rt,0); return rt; } int kth_min_idx_INF(int rt,int k) { pushdown(rt); if(rt==0) { return 0; } if(k<=tree[lson(rt)].siz) { return kth_min_idx_INF(lson(rt),k); } else { if(tree[lson(rt)].siz+tree[rt].cnt<k) { return kth_min_idx_INF(rson(rt),k-tree[lson(rt)].siz-tree[rt].cnt); } else { return rt; } } } int kth_min_idx(int rt,int k) { return kth_min_idx_INF(rt,k+1); } int split(int l,int r) { l=kth_min_idx(root,l-1); r=kth_min_idx(root,r+1); splay(l,0); splay(r,l); return lson(rson(l)); } void reverse(int l,int r) { tree[split(l,r)].lazy^=1; } int query(int val) { splay(val,0); return tree[lson(root)].siz-1+1; } }T; int main() { int n,ans,i; cin>>n; for(i=1;i<=n;i++) { cin>>a[i].first; a[i].second=T.insert(i); } sort(a+1,a+1+n); for(i=1;i<=n;i++) { ans=T.query(a[i].second); T.reverse(i,ans); cout<<ans<<" "; } return 0; }
luogu P4146 序列终结者
-
额外维护一个增量标记即可,由于
pushup
时涉及到对左右儿子节点(不一定存在)取 \(\max\) ,注意特判即可。点击查看代码
struct BST { const int INF=0x7f7f7f7f; int rt_sum,root; struct FHQ_Treap { int son[2],idx,val,rnd,cnt,siz,lazy_add,lazy_reverse,maxx; }tree[100010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=root=0; srand(time(0)); } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; tree[rt].maxx=tree[rt].val; if(lson(rt)!=0) { tree[rt].maxx=max(tree[rt].maxx,tree[lson(rt)].maxx); } if(rson(rt)!=0) { tree[rt].maxx=max(tree[rt].maxx,tree[rson(rt)].maxx); } } int build(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].lazy_add=tree[rt_sum].lazy_reverse=0; tree[rt_sum].idx=val; tree[rt_sum].val=tree[rt_sum].maxx=0; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushdown(int rt) { if(rt!=0&&tree[rt].lazy_reverse!=0) { swap(lson(rt),rson(rt)); tree[lson(rt)].lazy_reverse^=1; tree[rson(rt)].lazy_reverse^=1; tree[rt].lazy_reverse=0; } if(rt!=0&&tree[rt].lazy_add!=0) { tree[lson(rt)].val+=tree[rt].lazy_add; tree[rson(rt)].val+=tree[rt].lazy_add; tree[lson(rt)].maxx+=tree[rt].lazy_add; tree[rson(rt)].maxx+=tree[rt].lazy_add; tree[lson(rt)].lazy_add+=tree[rt].lazy_add; tree[rson(rt)].lazy_add+=tree[rt].lazy_add; tree[rt].lazy_add=0; } } void split(int rt,int k,int &ls,int &rs) { if(rt==0) { ls=rs=0; return; } pushdown(rt); if(tree[lson(rt)].siz+tree[rt].cnt<=k) { ls=rt; split(rson(ls),k-tree[lson(rt)].siz-tree[rt].cnt,rson(ls),rs); } else { rs=rt; split(lson(rs),k,ls,lson(rs)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) { return rt1+rt2; } if(tree[rt1].rnd<tree[rt2].rnd) { pushdown(rt1); rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { pushdown(rt2); lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } void insert(int val) { int ls,rs; split(root,val,ls,rs); root=merge(merge(ls,build(val)),rs); } void update(int l,int r,int val) { int ls,rs,mid; split(root,r,ls,rs); split(ls,l-1,ls,mid); tree[mid].val+=val; tree[mid].maxx+=val; tree[mid].lazy_add+=val; root=merge(merge(ls,mid),rs); } void reverse(int l,int r) { int ls,rs,mid; split(root,r,ls,rs); split(ls,l-1,ls,mid); tree[mid].lazy_reverse^=1; root=merge(merge(ls,mid),rs); } int query(int l,int r) { int ls,rs,mid,ans; split(root,r,ls,rs); split(ls,l-1,ls,mid); ans=tree[mid].maxx; root=merge(merge(ls,mid),rs); return ans; } }T; int main() { int n,m,i,pd,l,r,val; cin>>n>>m; for(i=1;i<=n;i++) { T.insert(i); } for(i=1;i<=m;i++) { cin>>pd>>l>>r; if(pd==1) { cin>>val; T.update(l,r,val); } if(pd==2) { T.reverse(l,r); } if(pd==3) { cout<<T.query(l,r)<<endl; } } return 0; }
UVA11922 Permutation Transformer
-
由于需要翻转后放到末尾,故 \(FHQ-Treap\) 在合并时跳着合并即可。
点击查看代码
struct BST { const int INF=0x7f7f7f7f; int rt_sum,root; struct FHQ_Treap { int son[2],val,rnd,cnt,siz,lazy; }tree[100010]; #define lson(rt) (tree[rt].son[0]) #define rson(rt) (tree[rt].son[1]) BST() { rt_sum=root=0; srand(time(0)); } void pushup(int rt) { tree[rt].siz=tree[lson(rt)].siz+tree[rson(rt)].siz+tree[rt].cnt; } int build(int val) { rt_sum++; lson(rt_sum)=rson(rt_sum)=tree[rt_sum].lazy=0; tree[rt_sum].val=val; tree[rt_sum].rnd=rand(); tree[rt_sum].cnt=tree[rt_sum].siz=1; return rt_sum; } void pushdown(int rt) { if(rt!=0&&tree[rt].lazy!=0) { swap(lson(rt),rson(rt)); tree[lson(rt)].lazy^=1; tree[rson(rt)].lazy^=1; tree[rt].lazy=0; } } void split(int rt,int k,int &ls,int &rs) { if(rt==0) { ls=rs=0; return; } pushdown(rt); if(tree[lson(rt)].siz+tree[rt].cnt<=k) { ls=rt; split(rson(ls),k-tree[lson(rt)].siz-tree[rt].cnt,rson(ls),rs); } else { rs=rt; split(lson(rs),k,ls,lson(rs)); } pushup(rt); } int merge(int rt1,int rt2) { if(rt1==0||rt2==0) { return rt1+rt2; } if(tree[rt1].rnd<tree[rt2].rnd) { pushdown(rt1); rson(rt1)=merge(rson(rt1),rt2); pushup(rt1); return rt1; } else { pushdown(rt2); lson(rt2)=merge(rt1,lson(rt2)); pushup(rt2); return rt2; } } void insert(int val) { int ls,rs; split(root,val,ls,rs); root=merge(merge(ls,build(val)),rs); } void reverse(int l,int r) { int ls,rs,mid; split(root,r,ls,rs); split(ls,l-1,ls,mid); tree[mid].lazy^=1; root=merge(merge(ls,rs),mid); } void inorder(int rt) { if(rt!=0) { pushdown(rt); inorder(lson(rt)); cout<<tree[rt].val<<endl; inorder(rson(rt)); } } }T; int main() { int n,m,l,r,i; cin>>n>>m; for(i=1;i<=n;i++) { T.insert(i); } for(i=1;i<=m;i++) { cin>>l>>r; T.reverse(l,r); } T.inorder(T.root); return 0; }
5.23
闲话
- 早上到机房后,发现门上已经把昨天划的痕迹的区域给割下来了,然后就安上了木头边框,用胶水固定了玻璃。但 \(3\) 机房又用语文习字纸/作文升格纸盖上了,没什么意义。
- 物理课现物理老师让我们改一改不写作业的习惯。
- 详见 2024 HE中考 游记 5.23 。
做题纪要
BZOJ2839 集合计数
-
考虑二项式反演。
-
设 \(f_{m}\) 表示交集中恰好有 \(m\) 个元素的方案数, \(g_{m}\) 表示交集中至少有 \(m\) 个元素的方案数,即 \(g_{m}=\sum\limits_{i=m}^{n}\dbinom{i}{m}f_{i}\) ,那么有 \(f_{m}=\sum\limits_{i=m}^{n}(-1)^{i-m}\dbinom{i}{m}g_{i}\) 。
-
现在问题转化为怎么求 \(g_{m}\) 。先钦定选出的 \(m\) 个元素,此时方案数为 \(\dbinom{n}{m}\) ;接着剩下的 \(n-m\) 个元素一共可以组成 \(2^{n-m}\) 个集合(包括空集),又因为这些集合中至少要选 \(1\) 个,故状态转移方程为 \(g_{m}=\dbinom{n}{m}(2^{2^{n-m}}-1)\) 。
-
由 \(\begin{aligned} 2^{2^{n-m+1}}=2^{2^{n-m} \times 2}=2^{2^{n-m}+2^{n-m}}=(2^{2^{n-m}})^{2} \end{aligned}\) 倒序处理即可。
点击查看代码
const ll p=1000000007; ll g[1000010],jc[1000010],inv[1000010],jc_inv[1000010]; ll C(ll n,ll m,ll p) { return (n>=m&&n>=0&&m>=0)?((jc[n]*jc_inv[m]%p)*jc_inv[n-m]%p):0; } int main() { ll n,k,i,mi=2,f=0; cin>>n>>k; inv[1]=1; jc[0]=jc_inv[0]=jc[1]=jc_inv[1]=1; for(i=2;i<=n;i++) { inv[i]=(p-p/i)*inv[p%i]%p; jc[i]=jc[i-1]*i%p; jc_inv[i]=jc_inv[i-1]*inv[i]%p; } for(i=n;i>=k;i--) { g[i]=C(n,i,p)*mi%p; mi=mi*mi%p; } for(i=k;i<=n;i++) { f=(f+((i-k)%2==0?1:-1)*C(i,k,p)*g[i]%p+p)%p; } cout<<f<<endl; return 0; }
BZOJ3462 DZY Loves Math II
-
因为有条件 \((2)\) 的限制,故条件 \((4)\) 成立的前提是对 \(S\) 的质因数分解后,不存在一个素数的指数 \(>1\) 。
-
由算术基本定理,设 \(S=\sum\limits_{i=1}^{k}p_{i}^{c_{i}},n=\sum\limits_{i=1}^{k}p_{i}t_{i}\) ,其中 \(t_{i} \ge 1\) 。
-
由于 \(n\) 很大,故直接完全背包无法维护。现让 \(n\) 减去 \(\sum\limits_{i=1}^{k}p_{i}\) ,此时只需要满足 \(t_{i} \ge 0\) 即可。
-
设 \(\begin{cases} x_{i}=\left\lfloor \frac{p_{i}t_{i}}{S} \right\rfloor \\ y_{i}=\frac{p_{i}t_{i} \bmod S}{p_{i}} \in [0,\frac{S}{p_{i}}) \end{cases}\) ,此时 \(p_{i}t_{i}\) 可以改写为 \(x_{i}S+y_{i}p_{i}\) ,即 \(\begin{aligned} n=\sum\limits_{i=1}^{k}x_{i}S+y_{i}p_{i}=S\sum\limits_{i=1}^{k}x_{i}+\sum\limits_{i=1}^{k}y_{i}p_{i} \end{aligned}\) ,其中 \(\sum\limits_{i=1}^{k}y_{i}p_{i}<kS\) 。
-
接着考虑枚举 \(h \in [0,k)\) ,前半部分类似 luogu P1771 方程的解 ,方案数为 \(\dbinom{k+\left\lfloor \frac{n}{S} \right\rfloor-h-1}{k-1}\) ;后半部分由 \(\sum\limits_{i=1}^{k}y_{i}p_{i}=hS+n \bmod S\) 完全背包统计方案数即可。
- 严格来说是后半部分不存在能拼成 \(S\) 的情况。
点击查看代码
const ll p=1000000007; ll prime[100],c[100],inv[100],f[12000010],len=0; void divide(ll n) { for(ll i=2;i<=sqrt(n);i++) { if(n%i==0) { len++; prime[len]=i; c[len]=0; while(n%i==0) { n/=i; c[len]++; } } } if(n>1) { len++; prime[len]=n; c[len]=1; } } ll C(ll n,ll m,ll p) { ll ans=1,i; if(n>=m&&n>=0&&m>=0) { for(i=n-m+1;i<=n;i++) { ans=ans*(i%p)%p; } for(i=1;i<=m;i++) { ans=ans*inv[i]%p; } return ans; } else { return 0; } } int main() { ll s,q,n,flag=1,sum=0,ans=0,i,j; cin>>s>>q; divide(s); f[0]=1; for(i=1;i<=len;i++) { flag&=(c[i]==1); sum+=prime[i]; for(j=prime[i];j<=len*s;j++) { f[j]=(f[j]+f[j-prime[i]])%p; } for(j=len*s;j>=s;j--)//减去影响 { f[j]=(f[j]-f[j-s]+p)%p; } } inv[1]=1; for(i=2;i<=len;i++) { inv[i]=(p-p/i)*inv[p%i]%p; } for(i=1;i<=q;i++) { cin>>n; if(flag==0||sum>n) { cout<<"0"<<endl; } else { n-=sum; ans=0; for(j=0;j<=len-1&&j*s+n%s<=n;j++) { ans=(ans+C(len+(n/s-j)-1,len-1,p)*f[j*s+n%s]%p)%p; } cout<<ans<<endl; } } return 0; }
5.24
闲话
做题纪要
5.25
闲话
做题纪要
CF1141E Superhero Battle
-
等价于求一个最小的 \(m\) 使得 \(H+\sum\limits_{i=1}^{m}d_{(i-1) \bmod n+1} \le 0\) ,移项得到 \(\sum\limits_{i=1}^{m}-d_{(i-1) \bmod n+1} \ge H\) 。
-
将 \(\sum\limits_{i=1}^{m}-d_{(i-1) \bmod n+1}\) 拆成 \(x\sum\limits_{i=1}^{n}-d_{i}\) 和 \(\sum\limits_{i=1}^{y}-d_{i}\) 两部分,即 \(m=xn+y\) 。枚举 \(y \in [1,n]\) ,则对应的 \(\min \{ x \}=\left\lceil \frac{H-\sum\limits_{i=1}^{y}-d_{i}}{\sum\limits_{i=1}^{n}-d_{i}} \right\rceil\) 。
-
最终,有 \(\min\limits_{y=1}^{n} \{ \left\lceil \frac{H-\sum\limits_{i=1}^{y}-d_{i}}{\sum\limits_{i=1}^{n}-d_{i}} \right\rceil n+y \}\) 即为所求,注意精度影响,优化同 [ABC345B] Integer Division Returns 。
-
注意判无解时要对第一轮进行特判。
点击查看代码
ll d[200010],sum[200010]; int main() { ll h,n,ans=0x7f7f7f7f7f7f7f7f,flag=0,i; cin>>h>>n; for(i=1;i<=n;i++) { cin>>d[i]; sum[i]=sum[i-1]-d[i]; if(sum[i]>=h&&flag==0) { flag=1; cout<<i<<endl; } } if(flag==0) { if(sum[n]<=0) { cout<<"-1"<<endl; } else { for(i=1;i<=n;i++) { ans=min(ans,(h-sum[i]+sum[n]-1)/sum[n]*n+i); } cout<<ans<<endl; } } return 0; }
luogu P3330 [ZJOI2011] 看电影
-
由于只需要关注是否有人坐在位置上,故总方案数为 \(k^{n}\) 。
-
接着开始考虑如何计算合法方案数。考虑拉链成环,在最后增加第 \(k+1\) 个座位,类似圆排的推导过程,有此时的方案数为 \(\frac{(k+1)^n}{k+1}=(k+1)^{n-1}\) 。但是 \(k+1\) 这个位置上是不能坐第 \(1 \sim n\) 个人的,可以强制让第 \(k+1\) 个位置上坐第 \(n+1\) 个人,且由于是环,故 \(k+1\) 这个位置在哪里并不重要,只需要找随便找一个位置即可,此时的方案数为 \(k+1-n\) 。二者相乘即可。
- 为保证偶遇空位置,故 \(k+1-n \le 0\) 时无解。
-
最终有 \(\dfrac{(k+1)^{n-1}(k+1-n)}{k^{n}}\) 即为所求。
- 高精乘、除、模同 luogu P1932 A+B A-B A*B A/B A%B Problem 。
- 高精快速幂同 luogu P1045 [NOIP2003 普及组] 麦森数 。
- 高精 \(\gcd\) 同 luogu P2152 [SDOI2009] SuperGCD 。
点击查看代码
struct BigInteger { ll num[200]; void init() { memset(num,0,sizeof(num)); num[0]=1; } void print() { cout<<num[num[0]]; for(ll i=num[0]-1;i>=1;i--) { printf("%08lld",num[i]); } } BigInteger operator * (ll another) { BigInteger ans; ans.init(); ans.num[0]=num[0]; for(ll i=1;i<=num[0];i++) { ans.num[i]+=num[i]*another; ans.num[i+1]+=ans.num[i]/100000000; ans.num[i]%=100000000; } while(ans.num[ans.num[0]+1]!=0) { ans.num[0]++; } return ans; } BigInteger operator * (BigInteger another) { BigInteger ans; ans.init(); ans.num[0]=num[0]+another.num[0]-1; for(ll i=1;i<=num[0];i++) { for(ll j=1;j<=another.num[0];j++) { ans.num[i+j-1]+=num[i]*another.num[j]; ans.num[i+j-1+1]+=ans.num[i+j-1]/100000000; ans.num[i+j-1]%=100000000; } } while(ans.num[ans.num[0]+1]!=0) { ans.num[0]++; } return ans; } BigInteger operator / (ll another) { BigInteger ans; ans.init(); ans.num[0]=num[0]; for(ll i=1;i<=num[0];i++) { ans.num[i]=num[i]; } for(ll i=num[0];i>=1;i--) { if(i-1>=1) { ans.num[i-1]+=ans.num[i]%another*100000000; } ans.num[i]/=another; } while(ans.num[0]>1&&ans.num[ans.num[0]]==0) { ans.num[0]--; } return ans; } ll operator % (ll another) { ll ans=0; for(ll i=num[0];i>=1;i--) { ans=(ans*100000000%another+num[i]%another)%another; } return ans; } }x,y; ll gcd(ll a,ll b) { return b?gcd(b,a%b):a; } BigInteger qpow(BigInteger a,ll b) { BigInteger ans; ans.init(); ans.num[1]=1; while(b>0) { if(b&1) { ans=ans*a; } b>>=1; a=a*a; } return ans; } int main() { ll t,n,k,d,i; cin>>t; for(i=1;i<=t;i++) { cin>>n>>k; if(k+1-n<=0) { cout<<"0 1"<<endl; } else { x.init(); y.init(); x.num[1]=k+1; x=qpow(x,n-1); x=x*(k+1-n); y.num[1]=k; y=qpow(y,n); d=gcd(k+1-n,y%(k+1-n)); x=x/d; y=y/d; x.print(); cout<<" "; y.print(); cout<<endl; } } return 0; }
HZOJ 571.摸鱼
-
此时再 \(O(c)\) 枚举不太可做,考虑其他做法。
- 虽然说数据过水,可以水过去。
-
类似 CF1141E Superhero Battle ,我们仍将 \(m\) 拆成 \(m=x(c+d)+y\) 两部分,其中 \(y \le c\) 。由于前 \(c\) 项都是正整数,故从贪心的角度分析,在 \(y\) 大的同时 \(x\) 尽可能小即可,有 \(\left\lceil \frac{s-(a-b)c}{(a-b)c-bd} \right\rceil(c+d)+\left\lceil \frac{s-\left\lceil \frac{s-(a-b)c}{(a-b)c-bd} \right\rceil((a-b)c-bd)}{a-b} \right\rceil\) 即为所求。
-
对第一轮进行特判。
点击查看代码
int main() { ll s,a,b,c,d,x,y,sum,ans; cin>>s>>a>>b>>c>>d; if((a-b)*c>=s) { ans=ceil(1.0*s/(a-b)); } else { if(c*(a-b)-d*b<=0) { ans=-1; } else { sum=(a-b)*c-b*d; x=ceil(1.0*(s-(a-b)*c)/sum); y=ceil(1.0*(s-x*sum)/(a-b)); ans=x*(c+d)+y; } } cout<<ans<<endl; return 0; }
5.26
闲话
- 详见 2024 HE中考 游记 5.26 。
做题纪要
[ABC350D] New Friends
-
设一共有 \(k\) 个连通块,则 \(\sum\limits_{i=1}^{k}\frac{siz_{k}(siz_{k}-1)}{2}-m\) 即为所求。
点击查看代码
struct node { ll nxt,to; }e[400010]; ll head[400010],vis[400010],cnt=0,num; void add(ll u,ll v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(ll x) { num++; vis[x]=1; for(ll i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { dfs(e[i].to); } } } int main() { ll n,m,u,v,sum=0,i; cin>>n>>m; for(i=1;i<=m;i++) { cin>>u>>v; add(u,v); add(v,u); } for(i=1;i<=n;i++) { if(vis[i]==0) { num=0; dfs(i); sum+=num*(num-1)/2; } } cout<<sum-m<<endl; return 0; }
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18204213,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。