前言:昨天晚上没睡好,导致今天早上起来头疼,于是睡了一多个小时,本来不想比赛了,但是发现第一题的做法实在是太显而易见,于是就愉快的AC了
T1
很简单的一道线段树问题
天天去哪吃
题目大意
小天在第i天去了第x[i]号餐厅吃饭,并且在接下来的n/2天内都不能再去这个餐厅去吃饭了,$x[i]=(a*x[i-1]+b*x[i-2]+k)\%n$ (k是能使x[i]合法的最小值,即x[i]在x[i-n/2~i-1]中没有出现过)
输入
n,m,a,b,x[1],x[2]
输出
x[3],x[4]……x[m]
注意到数据之间的关联(大小关系,这个技巧简直太好用了),还有我们需要求解的问题(快速查找$(a*x[i-1]+b*x[i-2]+k)\%n$在剩余系下在它右边距离最近的合法值),而且还要支持单点修改(把x[i-n/2-1]加入,再把x[i]删除),很明显这是一颗线段树……所以$O(mlogn)$
但是有个问题,如果$(a*x[i-1]+b*x[i-2]+k)\%n$到$n$之间的所有数都被删完了怎么办,由于剩余系的原因我们还需要从1开始向右接着找……好像有点麻烦是不是?
当然其实我们还有更好的方法,因为剩余系可以抽象成一个环,所以我们可以把这个环破开变成长度为2*n的线段,每次修改x[i]的时候只要同时修改x[i]+n即可
但是还有个问题(问题怎么这么多QWQ),如果查找的过程在线段上跨越了超过n的长度怎么办?其实不用担心,因为剩余系里最多只能同时删掉n/2个数,所以最多只需要跨越n/2的长度就会找到我们需要的数了
后记:果然是自己想太多了……随机数据直接模拟就能拿100分
1 #include <cstdio> 2 #include <iostream> 3 using namespace std; 4 struct T{ 5 int l,r,f,w; 6 }t[200001*4]; 7 int n,n_2,m,q,f[200001]; 8 long long a,b; 9 int build(int x,int l,int r) 10 { 11 t[x].l=l,t[x].r=r,t[x].w=r-l+1; 12 if (l==r) return t[x].w; 13 int mid=(l+r)/2; 14 build(x*2,l,mid);build(x*2+1,mid+1,r); 15 } 16 int query(int x,int l) 17 { 18 if (t[x].w==0||t[x].r<l) return -1; 19 if (t[x].l==t[x].r) return t[x].l; 20 int ret=query(x*2,l); 21 if (ret>=0) return ret; 22 return query(x*2+1,l); 23 } 24 void update(int x,int l,int upd) 25 { 26 if (l<t[x].l||l>t[x].r) return; 27 t[x].w+=upd; 28 if (t[x].l==t[x].r) return; 29 update(x*2,l,upd);update(x*2+1,l,upd); 30 } 31 int main() 32 { 33 freopen("lunch.in","r",stdin); 34 freopen("lunch.out","w",stdout); 35 ios::sync_with_stdio(false); 36 cin>>n>>m>>a>>b>>f[1]>>f[2]; 37 build(1,0,2*n-1); 38 update(1,f[1],-1);update(1,f[1]+n,-1); 39 update(1,f[2],-1);update(1,f[2]+n,-1); 40 n_2=n/2; 41 for (int i=3;i<=m;i++) 42 { 43 if (i-n_2-1>=1){update(1,f[i-n_2-1],1);update(1,f[i-n_2-1]+n,1);} 44 f[i]=query(1,int((a*f[i-1]+b*f[i-2])%n))%n; 45 update(1,f[i],-1);update(1,f[i]+n,-1); 46 cout<<f[i]<<" "; 47 } 48 }
T2
二分答案k,一眼就看出来,可是没时间写了
fyz的另一种思路:直接找最长路即可(类似有机物命名)