2.19比赛
非常非常非常NOIp的模拟赛(吐槽完毕)
【问题描述】
小X现在正准备刷墙,墙的长度为n。
但是刷墙不是一蹴而就的,所以他会刷m次墙,每次刷的是墙的连续的一段,有时候也会覆盖之前刷的部分。
终于,墙刷完了,先在小X想问你从 1~n 块墙分别是什么时候刷的
【输入格式】
输入文件名为paint.in
第一行两个数n,m表示墙的长度和刷墙次数
第二行两个数p,q表示种子
第i次询问的两个端点分别是 (i*p+q)%n+1 , (i*q+p)%n+1
【输出格式】
输出文件名为paint.out
共n行,每行一个整数表示第i块墙最后被刷的时间,如果没被刷到输出0
【输入样例】
4 3
2 4
【输出样例】
2
3
3
0
【数据规模与约定】
对于30%的数据, 1<=n,m<=3000
对于50%的数据, 1<=n<=8000,m<=104
对于80%的数据,1<=n<=5*105,m<=106
对于100%的数据,1<=n<=106,1<=m<=107
数据保证运算过程中不会爆int
本题输出较大,建议使用所提供的快输
原题:洛谷 2391白雪皑皑
sol:首先经过漫长的思考,发现正着做怎么也弄不了
于是就考虑倒过来做:此时惊喜的发现每个点只要染色一次就OK了,这就用并查集随便处理一下,如果x被染色了,Father[x]就是x+1,下次直接跳过就可以了
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=10000005; int n,m,seed1,seed2; int Cor[N]; int Father[N]; inline int Get_Father(int x) { int Fa=Father[x]; while(Fa!=Father[Fa]) Fa=Father[Fa]; while(x!=Father[x]) { int oo=x; x=Father[x]; Father[oo]=Fa; } return Fa; } int main() { freopen("paint.in","r",stdin); freopen("paint.out","w",stdout); int i,j; R(n); R(m); R(seed1); R(seed2); for(i=1;i<=n+1;i++) Father[i]=i; for(i=m;i>=1;i--) { int l=((long long)(seed1*i+seed2))%n+1,r=((long long)(seed2*i+seed1))%n+1; if(l>r) swap(l,r); for(j=l;j<=r;) { int Fa=Get_Father(j); if(Fa==j) { Cor[j]=i; Father[j]=j+1; } j=Fa; } } for(i=1;i<=n;i++) { Wl(Cor[i]); } return 0; } /* input 4 3 2 4 output 2 3 3 0 */
【问题描述】
并查集真好用,现在有n个并查集,围成了一圈,每个并查集有一个ac值。
你必须从中取出m个并查集,使他们的ai和最大,这样才可以ac此题。
值得一提的是你不可以选择两个相邻的并查集(1和n也相连),这样他们会连在一起,产生爆0气息。
【输入格式】
输入文件名为plant.in
输入的第一行包含两个正整数n,m,分别表示并查集数和所选并查集数。
第二行n个整数ai,表示第i个并查集的ac值。
【输出格式】
输出文件名为plant.out
输出一个整数,表示最佳方案可以得到的ac值。如果无解输出“Error!”,不包含引号。
【输入样例】
7 3
1 2 3 4 5 6 7
【输出样例】
15
【数据规模与约定】
对于40%的数据,1<=n<=70
对于55%的数据,1<=n<=200
对于85%的数据,1<=n<=2000
对于全部数据,1<=n<=2*105,-1000<=ai<=1000
sol:原题是 51nod 夹克老爷的逢三抽一,思路清奇
用的是堆贪心,其精髓在于反悔机制(摘自yj大佬原话),如果你选了x,则x-1和x+1都不在了,链表随便搞搞,然后在堆中加入一个ax-1+ax+1-ax
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=500005; int n,m,Val[N]; int L[N],R[N]; typedef pair<int,int> P; set<P>S; inline void Del(int x) { S.erase(P(Val[x],x)); L[R[x]]=L[x]; R[L[x]]=R[x]; return; } int main() { freopen("plant.in","r",stdin); freopen("plant.out","w",stdout); int i,ans=0; R(n); R(m); if(m>(n>>1)) return 0*puts("Error!"); for(i=1;i<=n;i++) { R(Val[i]); S.insert(P(Val[i],i)); L[i]=(i-1+n-1)%n+1; R[i]=(i+1-1)%n+1; } for(i=1;i<=m;i++) { int Pos=S.rbegin()->second; S.erase(*S.rbegin()); int k1=Val[L[Pos]],k=Val[Pos],k2=Val[R[Pos]]; ans+=k; Del(L[Pos]); Del(R[Pos]); Val[Pos]=k1+k2-k; S.insert(P(Val[Pos],Pos)); } Wl(ans); return 0; } /* input 7 3 1 2 3 4 5 6 7 output 15 */
【问题描述】
小W现在要去S国ak,S国有n个城市,但是各地之间的路还没有修好,所以他决定在ak的同时,也会参与道路的建设。
所以现在有m个时刻,每个时刻可能会有两个城市之间的道路修好了,也有可能小W问你最早什么时候他可以从u城市到v城市
【输入格式】
输入文件名为build.in
第一行两个整数 n,m。
接下来m行,每行0 u v 或 1 u v的形式
0 u v 表示这两个城市之间建立了新的道路
1 u v 表示小W的询问
小W为了考验你,特地对数据精心加密,对于每次操作,真正的u,v都等于读入的u,v异或上一次的答案,一开始这个值为0。
【输出格式】
输出文件名为build.out
对于每次询问,输出 u, v 最早在加入哪条边后可以互相到达,若到这个操作时还没联通,则输出 0。
【输入样例】
5 9
0 1 4
1 2 5
0 2 4
0 3 4
1 3 1
0 7 0
0 6 1
0 1 6
1 2 6
【输出样例】
0
3
5
【数据规模与约定】
对于30%的数据, 1<=n<=5000
对于60%的数据,1<=n<=30000
对于80%的数据,1<=n<=100000
对于100%的数据,1<=n<=5*105
sol:这大概是最水的一道了(不知道原题)
强制在线用并查集做,对于x,y:直接按秩合并,树高就一直是log了,然后暴力找x,y之间最晚的那条边就好了
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=500005; int n,m; struct BingCJ { int Father[N],G[N],Depth[N]; inline int Get_Father(int x) { if(Father[x]==x) { Depth[x]=1; return x; } else { int oo=Get_Father(Father[x]); Depth[x]=Depth[Father[x]]+1; return oo; } } inline void Merge(int x,int y,int T) { register int xx=Get_Father(x),yy=Get_Father(y); if(xx==yy) return; if(Depth[xx]<Depth[yy]) swap(xx,yy); Father[yy]=xx; G[yy]=T; if(Depth[xx]=Depth[yy]) Depth[xx]++; return; } inline int Solve(int x,int y) { register int xx=Get_Father(x),yy=Get_Father(y); if(xx!=yy) return 0; register int Dx=Depth[x],Dy=Depth[y]; register int ans=0; while(x!=y) { if(Dx>Dy) { ans=max(ans,G[x]); x=Father[x]; Dx--; } else { ans=max(ans,G[y]); y=Father[y]; Dy--; } } return ans; } inline void Init() { register int i; for(i=1;i<=n;i++) { Father[i]=i; Depth[i]=1; } } }B; int main() { freopen("build.in","r",stdin); freopen("build.out","w",stdout); register int i,cnt=0,ans=0; R(n); R(m); B.Init(); for(i=1;i<=m;i++) { int opt=read(); int x=read()^ans,y=read()^ans; switch (opt) { case 0: B.Merge(x,y,++cnt); break; case 1: ans=B.Solve(x,y); Wl(ans); break; default: break; } } return 0; }
【问题描述】
你在遗迹中发现了可以ak的秘宝——小W的身*证。
小W太厉害了,所以他有n张身份证,这n张身*证排成一个序列。
在遗迹中还有一本书,记录了身*证的用法,每个身*证有小W的一丝能量,能量也可能会造成伤害,你可以用一段连续的身*证发动一个大膜法,膜法的强度是身*证能量的和,同时膜法要求的数量是有限制的,不能太多也不能太少。
现在,你急需借助小W的力量,所以你必须尽快知道前K大的膜法强度之和
【输入格式】
输入文件名为identity.in
第一行包含四个正整数n,k,l,r。其中n为身*证的个数,为膜法的数量限制,和分别是膜法身*证所需数量的上下界
接下来n个整数表示第张身*证的能量
【输出格式】
输出文件名为identity.out
一个整数表示前K大的膜法强度之和
【输入样例】
8 4 1 6
1 -2 3 -4 5 -6 7 -8
【输出样例】
23
【数据规模与约定】
测试点 | N | K |
---|---|---|
1 | 10 | 100 |
2 | 1000 | 500,000 |
3 | 100,000 | 1 |
4 | 10,000 | 10,000 |
5 | 500,000 | 10,000 |
6 | 80,000 | 80,000 |
7 | 100,000 | 100,000 |
8 | 100,000 | 500,000 |
9 | 500,000 | 500,000 |
10 | 500,000 | 500,000 |
-1000<=ai<=1000,1<=L,R<=n
原题:洛谷2048超级钢琴
首先对于一个位置i,从i+L-1~i+R-1之间找到最大的,加入堆中(ST表轻松实现)
然后如果i~x这段算过了,就把i+L-1,x-1和x+1,i+R-1这两段加进来,弄k次,就好了
#include <bits/stdc++.h> using namespace std; typedef int ll; inline ll read() { ll s=0; bool f=0; char ch=' '; while(!isdigit(ch)) { f|=(ch=='-'); ch=getchar(); } while(isdigit(ch)) { s=(s<<3)+(s<<1)+(ch^48); ch=getchar(); } return (f)?(-s):(s); } #define R(x) x=read() inline void write(ll x) { if(x<0) { putchar('-'); x=-x; } if(x<10) { putchar(x+'0'); return; } write(x/10); putchar((x%10)+'0'); return; } #define W(x) write(x),putchar(' ') #define Wl(x) write(x),putchar('\n') const int N=500005; int n,m; struct BingCJ { int Father[N],G[N],Depth[N]; inline int Get_Father(int x) { if(Father[x]==x) { Depth[x]=1; return x; } else { int oo=Get_Father(Father[x]); Depth[x]=Depth[Father[x]]+1; return oo; } } inline void Merge(int x,int y,int T) { register int xx=Get_Father(x),yy=Get_Father(y); if(xx==yy) return; if(Depth[xx]<Depth[yy]) swap(xx,yy); Father[yy]=xx; G[yy]=T; if(Depth[xx]=Depth[yy]) Depth[xx]++; return; } inline int Solve(int x,int y) { register int xx=Get_Father(x),yy=Get_Father(y); if(xx!=yy) return 0; register int Dx=Depth[x],Dy=Depth[y]; register int ans=0; while(x!=y) { if(Dx>Dy) { ans=max(ans,G[x]); x=Father[x]; Dx--; } else { ans=max(ans,G[y]); y=Father[y]; Dy--; } } return ans; } inline void Init() { register int i; for(i=1;i<=n;i++) { Father[i]=i; Depth[i]=1; } } }B; int main() { freopen("build.in","r",stdin); freopen("build.out","w",stdout); register int i,cnt=0,ans=0; R(n); R(m); B.Init(); for(i=1;i<=m;i++) { int opt=read(); int x=read()^ans,y=read()^ans; switch (opt) { case 0: B.Merge(x,y,++cnt); break; case 1: ans=B.Solve(x,y); Wl(ans); break; default: break; } } return 0; }