又是慈溪那边给的题目,此次终于没有像上次那样尴尬了,html
T1拿到了较高的暴力分,T2没写炸,而后T3写了一个优雅的暴力就203pts,Rank3了。git
据说其它学校的分数广泛100+,那咱们学校还不是强到飞起。数组
这题目出的真心长,让我联想到咱们语文老师说的:ui
之后教育改革可能不仅是语文,其余学科的考试题目字数都要显著增长了,到时后数学大题你看都看不完。spa
原来连OI的题目都有这种趋势。code
咱们精简题意后发现:给你两段序列,求两两乘积的第\(k\)大值。htm
而后瞄一眼数据范围,而后我就想到了一种经典的堆的解决方法。blog
先排序,而后把全部的\(a_i\cdot b_1\)的值都扔到大根堆里,而后每次取出最大的值以后就把\(b_i\)的下标变成\(i+1\)再乘起来扔回去便可。排序
复杂度是\(O(q\cdot k\ log\ k)\)的,确定会炸。ip
这里给出堆的代码(至于又开了小根堆是为了在\(k\)和\((b-a+1)(r-l+1)-k+1\)中取一个较小的)
53ptsCODE
#include<cstdio> #include<cctype> #include<algorithm> #include<queue> using namespace std; const int N=255,M=100005; int n,m,a[N],b[M],c[N],d[M],q,opt,L,R,l,r,k; struct Small { int x,y,s; bool operator <(const Small a) const { return a.s<s; } }; struct Big { int x,y,s; bool operator <(const Big a) const { return a.s>s; } }; priority_queue <Small> small; priority_queue <Big> big; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline void copy(void) { register int i; for (i=l;i<=r;++i) c[i-l+1]=a[i]; for (i=L;i<=R;++i) d[i-L+1]=b[i]; } inline bool small_cmp(int a,int b) { return a<b; } inline bool big_cmp(int a,int b) { return a>b; } inline void Big_solve(int k) { sort(c+1,c+r-l+2,big_cmp); sort(d+1,d+R-L+2,big_cmp); while (!big.empty()) big.pop(); for (register int i=1;i<=r-l+1;++i) big.push((Big){i,1,c[i]*d[1]}); for (;;) { Big now=big.top(); big.pop(); if (!(--k)) { write(now.s); putchar('\n'); return; } if (now.y^(R-L+1)) big.push((Big){now.x,now.y+1,c[now.x]*d[now.y+1]}); } } inline void Small_solve(int k) { sort(c+1,c+r-l+2,small_cmp); sort(d+1,d+R-L+2,small_cmp); while (!small.empty()) small.pop(); for (register int i=1;i<=r-l+1;++i) small.push((Small){i,1,c[i]*d[1]}); for (;;) { Small now=small.top(); small.pop(); if (!(--k)) { write(now.s); putchar('\n'); return; } if (now.y^(R-L+1)) small.push((Small){now.x,now.y+1,c[now.x]*d[now.y+1]}); } } int main() { freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout); register int i; read(n); read(m); for (i=1;i<=n;++i) read(a[i]); for (i=1;i<=m;++i) read(b[i]); read(q); while (q--) { read(opt); read(l); read(r); read(L); if (opt) { read(R); read(k); int tot=(r-l+1)*(R-L+1); copy(); if (k<tot-k+1) Big_solve(k); else Small_solve(tot-k+1); } else { if (l) b[r]=L; else a[r]=L; } } return 0; }
其实这是一个经典的模板(我怎么没据说过),咱们首先二分答案\(x\),而后对于其中一个数组排序。
以后对于另外一个数组中的全部数只须要再次二分统计个数便可。
主要仍是一个套路题。
CODE
#include<cstdio> #include<cctype> #include<algorithm> using namespace std; const int N=255,M=100005; int n,m,a[N],b[M],c[M],q,opt,L,R,l,r,k; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void write(int x) { if (x>9) write(x/10); putchar(x%10+'0'); } inline bool cmp(int a,int b) { return a>b; } inline void copy(void) { register int i; for (i=L;i<=R;++i) c[i-L+1]=b[i]; sort(c+1,c+R-L+2,cmp); } inline int check(int x) { int rk=0; for (register int i=l;i<=r;++i) { int l_=1,r_=R-L+1; while (l_<=r_) { int mid=l_+r_>>1; if (c[mid]*a[i]<x) r_=mid-1; else l_=mid+1; } rk+=r_; } return rk; } inline void solve(void) { int l=1,r=2e9; while (l<=r) { int mid=(r-l>>1)+l; if (check(mid)<k) r=mid-1; else l=mid+1; } write(r); putchar('\n'); } int main() { freopen("equipment.in","r",stdin); freopen("equipment.out","w",stdout); register int i; read(n); read(m); for (i=1;i<=n;++i) read(a[i]); for (i=1;i<=m;++i) read(b[i]); read(q); while (q--) { read(opt); read(l); read(r); read(L); if (opt) read(R),read(k),copy(),solve(); else l?b[r]=L:a[r]=L; } return 0; }
经典水题,听说是NOI的题目。良心签到不解释
对于这种层级分明的关系,考虑拓扑+DP转移。
咱们令\(f_i\)表示以\(i\)结尾的食物链的条数,发现转移时:
\(f_{son[i]}+=f_i\)
而后写一个拓扑转移便可,最后对于出度为\(0\)的点进行特判便可。
可是注意一点:单节点不构成食物链
CODE
#include<cstdio> #include<cctype> #include<cstring> using namespace std; const int N=100005; struct edge { int to,next; }e[N<<1]; int head[N],in[N],out[N],q[N],n,m,x,y,cnt; unsigned long long f[N],ans; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; } inline void reset(int now) { for (register int i=head[now];i!=-1;i=e[i].next) ++f[e[i].to]; } inline void top_sort(void) { register int i; int H=0,T=0; for (i=1;i<=n;++i) if (!in[i]) q[++T]=i,reset(i); while (H<T) { int now=q[++H]; for (register int i=head[now];i!=-1;i=e[i].next) { f[e[i].to]+=f[now]; if (!(--in[e[i].to])) q[++T]=e[i].to; } } } int main() { freopen("chain.in","r",stdin); freopen("chain.out","w",stdout); register int i; read(n); read(m); memset(head,-1,sizeof(head)); memset(e,-1,sizeof(e)); for (i=1;i<=m;++i) { read(x); read(y); add(x,y); ++in[y]; ++out[x]; } for (top_sort(),i=1;i<=n;++i) if (!out[i]) ans+=f[i]; return printf("%lld",ans),0; }
我去题目怎么又是那么长。
可是读懂题意以后你就会发现,这道题就是NOI2010 超级钢琴的树上版本。
对于超级钢琴,不会的同窗能够看一下sol,这里就再也不赘述。
而后在树上怎么弄,很简单的套路倍增。
考虑维护两个东西\(father_{i,j}\)表示\(i\)向上\(2^j\)个点的节点,\(f_{i,j}\)同理,表示的是最大值。
而后咱们肯定这条链下方的点,那么对于上面的点就要求\(sum_{father_{i,0}}\)最小了。仍是倍增\(log\)级别解决。
后面的堆等操做大同小异,其实关于树的题目倍增真的很万能。
CODE
#include<cstdio> #include<cctype> #include<queue> using namespace std; const int N=500005,P=25; int n,m,l,r,sum[N],father[N][P],dep[N]; long long ans; struct data { int s,l,r,t; bool operator <(const data x) const { return sum[x.s]-sum[father[x.t][0]]>sum[s]-sum[father[t][0]]; } }; struct RMQ { int x,num; }f[N][P]; priority_queue<data> big; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; int flag=1; while (!isdigit(ch=tc())) flag=ch^'-'?1:-1; while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); x*=flag; } inline int min(int a,int b) { return a<b?a:b; } inline void RMQ_init(void) { register int i,j; for (j=1;j<P;++j) for (i=1;i<=n;++i) father[i][j]=father[father[i][j-1]][j-1]; for (j=1;j<P;++j) for (i=1;i<=n;++i) if (dep[i]>=1<<j) f[i][j]=f[i][j-1].x<f[father[i][j-1]][j-1].x?f[i][j-1]:f[father[i][j-1]][j-1]; } inline int getfa(int x,int y) { for (register int i=P-1;i>=0;--i) if (y&(1<<i)) x=father[x][i]; return x; } inline int getmin(int x,int y) { RMQ MIN=f[x][0]; for (register int i=P-1;i>=0;--i) if (dep[father[y][i]]>=dep[x]) MIN=MIN.x<f[y][i].x?MIN:f[y][i],y=father[y][i]; return MIN.num; } int main() { freopen("oj.in","r",stdin); freopen("oj.out","w",stdout); register int i; read(n); for (i=1;i<=n;++i) read(father[i][0]); for (i=1;i<=n;++i) { read(sum[i]); sum[i]+=sum[father[i][0]]; dep[i]=dep[father[i][0]]+1; f[i][0]=(RMQ){sum[father[i][0]],i}; } read(m); read(l); read(r); RMQ_init(); for (i=1;i<=n;++i) if (dep[i]>=l) { int L=getfa(i,min(r-1,dep[i]-1)),R=getfa(i,l-1); big.push((data){i,L,R,getmin(L,R)}); } while (m--) { data now=big.top(); big.pop(); ans+=sum[now.s]-sum[father[now.t][0]]; if (now.l^now.t) big.push((data){now.s,now.l,father[now.t][0],getmin(now.l,father[now.t][0])}); if (now.r^now.t) { int next=dep[now.s]-dep[now.t]; next=getfa(now.s,next-1); big.push((data){now.s,next,now.r,getmin(next,now.r)}); } } return printf("%lld",ans),0; }