IOI 2021 集训队作业瞎做 Part 1

GK

对于 \(n\le 42\),直接折半搜索。

对于 \(n>42\),注意 \(a_1\) 不会很大(具体好像是不超过 \(2^{65-n}\) 之类的),所以直接枚举 \(a_1\)

考虑求解 \(a_1r\equiv b_1\pmod{q}\),其中 \(r\) 是奇数(与 \(q=2^{64}\) 互质)。

首先将 \(a_1,b_1\) 都除以最大公约数 \(g=\gcd(a_1,b_1)\)(当然模数 \(q\) 也要除以 \(\gcd(a_1,b_1,q)\) 变成 \(q'\))。那么 \(a_1\)\(b_1\) 不会同为偶数。而 \(a_1\) 不为偶数(若 \(a_1\) 是偶数,\(b_1\) 也是偶数,矛盾),\(b_1\) 也不为偶数(若 \(b_1\) 是偶数,\(a_1,r\) 至少一个是偶数,矛盾)。

那么就能求出在模 \(q'\) 意义下的逆元 \(v\)

回到模 \(q\) 意义。那么 \(r\) 一定是 \(vg+kq'\) 的形式。直接枚举所有可能的 \(r\) 即可。

\(k\) 只用枚举 \(\frac{q}{q'}\) 个,而 \(a_1\) 要满足一开始和 \(b_1\) 不会同为偶数的条件,\(\operatorname{lowbit}(a_1)=\operatorname{lowbit}(b_1)\)(同时可以发现 \(q'\) 实际上是常数),也就是 \(a_1\)\(\frac{q}{q'}\) 的倍数(还有更多限制,但不重要)。所以每个 \(a_1\) 均摊 \(O(1)\)

求完了倍数关系,那么还原出 \(a\) 序列,同时变一下 \(s\)。这个 \(a\) 序列的方案构造,从大到小贪心,能选就选。(因为若不选当前数,前面所有更小的数选了也补不回来)

总时间复杂度大概是 \(O(q^{1/3}\log q)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ull,ll> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ull read(){
	char ch=getchar();ull x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n;
ull b[maxn],s;
namespace small{
	int mid,pl1,pl2;
	ll ans;
	PII p1[2222222],p2[2222222];
	void dfs1(int dep,ull sum,ll cur){
		if(dep>mid) return p1[++pl1]=MP(sum,cur),void();
		dfs1(dep+1,sum+b[dep],cur|(1ll<<dep));
		dfs1(dep+1,sum,cur);
	}
	void dfs2(int dep,ull sum,ll cur){
		if(dep>n) return p2[++pl2]=MP(s-sum,cur),void();
		dfs2(dep+1,sum+b[dep],cur|(1ll<<dep));
		dfs2(dep+1,sum,cur);
	}
	void main(){
		if(n==1) return puts(s?"1":"0"),void();
		mid=n/2;
		dfs1(1,0,0);
		dfs2(mid+1,0,0);
		sort(p1+1,p1+pl1+1);
		sort(p2+1,p2+pl2+1);
		int cur=1;
		FOR(i,1,pl1){
			while(cur<=pl2 && p2[cur].first<p1[i].first) cur++;
			if(cur<=pl2 && p2[cur].first==p1[i].first){ans=p1[i].second|p2[cur].second;break;}
		}
		FOR(i,1,n) printf("%lld",(ans>>i)&1); 
	}
}
namespace big_or_small_that_is_a_question{
	ull a[maxn];
	int ans[maxn];
	ull gcd(ull a,ull b){return b?gcd(b,a%b):a;}
	void main(){
		int wtf=0;
		FOR(i,0,63) if((b[1]>>i)&1) break;
		else wtf++;
		FOR(_,1,1e9){
			ull g=gcd(_,b[1]);
			_/=g;b[1]/=g;
			if(_%2==0 || b[1]%2==0){_*=g;b[1]*=g;continue;}
			ull tmp=b[1],inv=1;
			FOR(i,1,63) if((tmp>>i)&1) inv|=1ull<<i,tmp+=b[1]<<i;
			inv*=_;
			_*=g;b[1]*=g; 
			FOR(i,0,(1<<wtf)-1){
				FOR(i,1,n) a[i]=b[i]*inv;
				ull tmps=s*inv;
				ROF(i,n,1) if(tmps>=a[i]) ans[i]=1,tmps-=a[i];
				else ans[i]=0;
				if(!tmps){
					FOR(i,1,n) printf("%d",ans[i]);
					return;
				}
				if(wtf) inv+=1ull<<(64-wtf);
			} 
		}
	}
}
int main(){
	n=read();
	FOR(i,1,n) b[i]=read();
	s=read();
	if(n<=42) small::main();
	else big_or_small_that_is_a_question::main();
}

MB [todo && starred]

必须好好记住 Hall 定理及其变式!

Hall 定理:对于一个两边点数相等的二分图,有完美匹配,当且仅当对于左边任意点集 \(A\),有 \(|N(A)|\ge|A|\)\(N(A)\) 为与 \(A\) 中至少一点相邻的点集)。

变式 1:对于一个两边点数均为 \(n\) 的二分图,最大匹配大小为 \(n+\min(|N(A)|-|A|)\)

变式 2:对于一个两边点数均为 \(n\) 的二分图,左边的一个点集 \(A\) 能被某个匹配覆盖,当且仅当对于任意 \(B\subseteq A\)\(|N(B)|\ge|B|\)

这题就要用到变式 2。

枚举左边一个集合,判断是否 \(|N(S)|\ge S\),然后高维前缀和即可知道每个点集是否可被覆盖。右边类似。

然后对于左右集合 \(A,B\),它们能被同一个匹配覆盖,当且仅当它们各自能被一个匹配覆盖。

必要性显然。

充分性考虑构造。把覆盖 \(A\) 的匹配中的边拎出来,变成左到右的有向边;\(B\) 的变成右向左。每个点出度是 \(1\),所以是个内向基环树森林。

那么对每条链或环隔一个取一个。因为原图是二分图,这样一定能做到取的边没有公共点。只有链尾的点可能没有被覆盖到,但出度是 \(0\),所以不在 \(A,B\) 中。

接下来不用讲了。

时间复杂度 \(O(n2^n+m2^m)\)

NC [todo]

被 pb 教育 marked。

首先,什么奇怪的东西都不要想,直接 \(f_{x,y}\) 表示 \((x,y)\) 能走到多少花。

然后,从右往左扫,用线段树维护每一行在此时这列的 \(f\)。先加框再加花。

加入框的右边界时,要把一段赋值成 0。

离开框的左边界时,要把一段和下面的最大值取 max。

加入花时,要把一段加 1。

冲就完了。

时间复杂度一个 log。

QE

考虑一组数最少要多少次变得相等。

若最大值是所有数的倍数,那么就是不等于最大值的数的个数(下文叫做第一种组);否则就是数的个数(下文叫做第二种组)。

回到原问题。显然,一开始相同的数在一组最优。

假如我们枚举了第一种组中所有的最大值,那么对于每一种数,若枚举了的最大值中有它的倍数,它就可以并到已存在的一组。最后不在任何一组中的数,就一起另开一组变成第二种组。

也就是说,若所有数都可以并到第一种组中,组数就是最大值个数,否则是最大值个数 \(+1\)。操作次数是 \(n-\) 所有最大值的次数之和。

前一种情况,注意到枚举的最大值肯定是某个集合的所有超集。这个集合就是从大到小贪心,若之前没有它的倍数就加进来。那么在选了这些数的基础上,把剩下的数按出现次数从大到小排序,贪心选择就可以使组数一定的前提下,操作次数最小。

后一种情况就更简单了,直接将所有数排序贪心。

时间复杂度 \(O(n+v\log v)\) 之类的。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=1000100,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
inline int qmo(int x){return x+(x>>31?mod:0);}
int n,m=1000000,a[maxn],cnt[maxn],tmp[maxn],tl,tmp2[maxn],tl2,tmp3[maxn],tl3,ans[maxn];
inline bool cmp(int x,int y){return cnt[x]>cnt[y];}
int main(){
	freopen("equal.in","r",stdin);
	freopen("equal.out","w",stdout);
	n=read();
	FOR(i,1,n) a[i]=read();
	sort(a+1,a+n+1);
	ROF(i,n,1){
		if(a[i]!=a[i+1]){
			bool flag=false;
			FOR(j,1,m/a[i]) if(cnt[a[i]*j]) flag=true;
			if(!flag) tmp[++tl]=a[i];
			else tmp2[++tl2]=a[i];
			tmp3[++tl3]=a[i];
		}
		cnt[a[i]]++;
	}
	sort(tmp+1,tmp+tl+1,cmp);
	sort(tmp2+1,tmp2+tl2+1,cmp);
	sort(tmp3+1,tmp3+tl3+1,cmp);
	FOR(i,0,n) ans[i]=1e9;
	int sum=n;
	ans[sum]=min(ans[sum],1);
	FOR(i,1,tl3){
		sum-=cnt[tmp3[i]];
		ans[sum]=min(ans[sum],i+1);
	}
	sum=n;
	FOR(i,1,tl) sum-=cnt[tmp[i]];
	ans[sum]=min(ans[sum],tl);
	FOR(i,1,tl2){
		sum-=cnt[tmp2[i]];
		ans[sum]=min(ans[sum],tl+i); 
	}
	FOR(i,1,n) ans[i]=min(ans[i],ans[i-1]);
	FOR(i,0,n) printf("%d ",ans[i]);
}

OL

数据范围这么奇怪,瞎枚举吧。枚举一堆点和打它们的顺序,每次把包含那个点的还存在的所有区间打掉。

为啥全打掉最优?如果留下了一些最大的,后面还要把它们打掉,费用可能会加。

虽然可能这次变少了,下次变多得不会太多,但是可以感性理解证明总有一种顺序,使得每次打掉全部最优。(如果出现了前半句那种情况,就完全可以把这两次交换顺序。)

然后就可以 DP 了。

\(f_{l,r}\) 表示把 \([l,r]\) 完全包含的区间全部打掉的最小代价。枚举下一个全部打掉的区间在哪个点即可转移。

时间复杂度 \(O(n^3)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=666,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,l[maxn],r[maxn],d[maxn],tmp[maxn],tl,f[maxn][maxn],p[maxn],mx[maxn];
inline bool cmp(int x,int y){return r[x]<r[y];} 
void solve(){
	n=read();
	tl=0;
	FOR(i,1,n){
		tmp[++tl]=l[i]=read();
		tmp[++tl]=r[i]=read();
		d[i]=read();
	}
	sort(tmp+1,tmp+tl+1);tl=unique(tmp+1,tmp+tl+1)-tmp-1;
	FOR(i,1,n){
		l[i]=lower_bound(tmp+1,tmp+tl+1,l[i])-tmp;
		r[i]=lower_bound(tmp+1,tmp+tl+1,r[i])-tmp;
	}
	FOR(i,1,n) p[i]=i;
	sort(p+1,p+n+1,cmp);
	ROF(i,tl-1,1){
		FOR(j,1,tl) mx[j]=0;
		int cur=1;
		FOR(j,i+1,tl){ 
			while(cur<=n && r[p[cur]]<=j){
				if(l[p[cur]]>=i) FOR(k,l[p[cur]],r[p[cur]]) mx[k]=max(mx[k],d[p[cur]]);
				cur++;
			}
			f[i][j]=1e9;
			FOR(k,i,j) f[i][j]=min(f[i][j],f[i][k-1]+f[k+1][j]+mx[k]);
		}
	}
	printf("%d\n",f[1][tl]);
}
int main(){
	int T=read();
	while(T--) solve();
}

LI

远古时期听过,然而没写,现在有点忘了,来填坑。

注意到若 \([l_1,r_1],[l_2,r_2]\) 都是连续段且交非空,那么交也是连续段(经典结论)。

所以若有多个连续段包含询问区间,可以交起来,一定也是连续段且包含询问区间。

显然这个连续段的 \(r\) 是所有包含询问区间的连续段中最小的。同时 \(l\) 是最大的。

把询问离线,然后枚举 \(r\),按照连续段套路地从左往右扫,每次求出此时最小的 \(l\),将这一段包含的询问的右端点和 \(r\)\(\min\)

其实由于 \(r\) 递增,直接设成 \(r\) 并以后不考虑,似乎复杂度也是对的。

倒过来再弄一次 \(l\)

upd:膜了发题解发现不用再做一遍,在上面设成 \(r\) 的时候也可以把左端点设成再一次询问的答案,并且以后不考虑。这也是用到了交的性质,也就是枚举更后面的 \(r\) 时,如果 \(l\) 仍然包含了这个区间,不会更后。

时间复杂度 \(O(n\log n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=100010,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
struct seg{
	int l,r,id;
	bool operator<(const seg &s)const{return r<s.r;}
}s[maxn];
struct cmp{
	bool operator()(const seg &s1,const seg &s2)const{return s1.l<s2.l;}
};
int n,m,p[maxn],ansl[maxn],ansr[maxn],stk1[maxn],tp1,stk2[maxn],tp2,mn[maxn*4],mnid[maxn*4],add[maxn*4];
priority_queue<seg,vector<seg>,cmp> pq;
inline void pushup(int o){
	mn[o]=min(mn[o<<1],mn[o<<1|1]);
	if(mn[o<<1|1]==mn[o]) mnid[o]=mnid[o<<1|1];
	else mnid[o]=mnid[o<<1];
}
inline void setadd(int o,int v){
	mn[o]+=v;
	add[o]+=v;
}
inline void pushdown(int o){
	if(add[o]){
		setadd(o<<1,add[o]);
		setadd(o<<1|1,add[o]);
		add[o]=0;
	}
}
void build(int o,int l,int r){
	if(l==r) return mn[o]=mnid[o]=l,void();
	int mid=(l+r)>>1;
	build(lson);build(rson);
	pushup(o);
}
void update(int o,int l,int r,int ql,int qr,int v){
	if(l>=ql && r<=qr) return setadd(o,v);
	pushdown(o);
	int mid=(l+r)>>1;
	if(mid>=ql) update(lson,ql,qr,v);
	if(mid<qr) update(rson,ql,qr,v);
	pushup(o);
}
int query(int o,int l,int r,int x){
	if(l==r) return l;
	pushdown(o);
	int mid=(l+r)>>1;
	if(mn[o<<1]==x) return query(lson,x);
	else return query(rson,x);
}
int query(int o,int l,int r,int ql,int qr,int x){
	if(l>=ql && r<=qr) return mn[o]==x?mnid[o]:-1;
	pushdown(o);
	int mid=(l+r)>>1;
	if(mid<ql) return query(rson,ql,qr,x);
	if(mid>=qr) return query(lson,ql,qr,x);
	return max(query(lson,ql,qr,x),query(rson,ql,qr,x));
}
int main(){
	n=read();
	FOR(i,1,n) p[i]=read();
	m=read();
	FOR(i,1,m) s[i].l=read(),s[i].r=read(),s[i].id=i;
	sort(s+1,s+m+1);
	build(1,1,n);
	int cur=1;
	FOR(i,1,n){
		while(tp1 && p[stk1[tp1]]>p[i]){
			update(1,1,n,stk1[tp1-1]+1,stk1[tp1],p[stk1[tp1]]);
			tp1--;
		}
		update(1,1,n,stk1[tp1]+1,i,-p[i]);
		stk1[++tp1]=i;
		while(tp2 && p[stk2[tp2]]<p[i]){
			update(1,1,n,stk2[tp2-1]+1,stk2[tp2],-p[stk2[tp2]]);
			tp2--;
		}
		update(1,1,n,stk2[tp2]+1,i,p[i]);
		stk2[++tp2]=i;
		int tmp=query(1,1,n,i);
		while(cur<=m && s[cur].r<=i) pq.push(s[cur++]);
		while(!pq.empty() && pq.top().l>=tmp){
			int id=pq.top().id;
			ansl[id]=query(1,1,n,1,pq.top().l,i);
			ansr[id]=i;
			pq.pop();
		}
	}
	FOR(i,1,m) printf("%d %d\n",ansl[i],ansr[i]);
}

FG

先全部倒过来,然后每个名字的生成方式变成了在后面加,于是可以弄出一个 Trie。询问变成了有多少个字符串的后缀是它。

离线,将所有询问串也加入这个 Trie 中,然后弄出一个 AC 自动机。

最后对于每个询问的答案,就是串的结束节点在 fail 树的子树中有多少个名字的结束节点。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=2222222,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,m,ch[maxn][26],cnt,at[maxn],fail[maxn],sz[maxn],q[maxn],h,r;
char tmp[1111111];
void insert(char *s,int id){
	int len=strlen(s+1),now=0;
	ROF(i,len,1){
		int p=s[i]-'A';
		if(!ch[now][p]) ch[now][p]=++cnt;
		now=ch[now][p];
	}
	at[id]=now;
}
void build(){
	h=1;r=0;
	FOR(i,0,25) if(ch[0][i]) q[++r]=ch[0][i];
	while(h<=r){
		int now=q[h++];
		FOR(i,0,25) if(ch[now][i]){
			fail[ch[now][i]]=ch[fail[now]][i];
			q[++r]=ch[now][i];
		}
		else ch[now][i]=ch[fail[now]][i];
	}
	ROF(i,r,1) sz[fail[q[i]]]+=sz[q[i]];
}
int main(){
	cnt=n=read();m=read();
	FOR(i,1,n){
		scanf("%s",tmp);
		int fa=read();
		ch[fa][tmp[0]-'A']=i;
		sz[i]=1;
	}
	FOR(i,1,m){
		scanf("%s",tmp+1);
		insert(tmp,i);
	}
	build();
	FOR(i,1,m) printf("%d\n",sz[at[i]]);
}

RI

计算几何和计算几何不能一概而论。

我曾在极度愤怒的情况下,用不到 1k 的代码切掉了集训队作业中的计算几何。

如果你被集训队作业中的重工业和高维计算几何劝退了,那么来这题,让你领略计算几何的真正魅力。

说得我都不信

显然整个多边形面积是整数。否则答案是 \(0\)

那么若枚举了不相邻的不同两点 \(i,j\),就要顺着叉积之和是偶数(面积是叉积之和的一半)。

然后式子是个 \(sum_j-sum_i-x_iy_j+x_jy_i\) 的形式,瞎搞即可。

时间复杂度 \(O(n)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=222222,mod=998244353;
#define MP make_pair
#define PB push_back
#define lson o<<1,l,mid
#define rson o<<1|1,mid+1,r
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,x[maxn],y[maxn],cnt[2][2][2];
ll ans;
int main(){
	freopen("integral.in","r",stdin);
	freopen("integral.out","w",stdout);
	n=read();
	FOR(i,1,n) x[i]=(read()%2+2)%2,y[i]=(read()%2+2)%2;
	int sum=0;
	FOR(i,1,n){
		if(i!=1) sum^=(x[i]&y[i-1])^(y[i]&x[i-1]);
		FOR(a,0,1) FOR(b,0,1) FOR(c,0,1) if(!(a^sum^(b&y[i])^(c&x[i]))) ans+=cnt[a][b][c];
		cnt[sum][x[i]][y[i]]++;
	}
	sum^=(x[1]&y[n])^(x[n]&y[1]);
	if(sum) puts("0");
	else printf("%lld\n",ans-n);
}

AK

% 铃酱 AK /se

为什么要标题一个 AK 啊

先考虑链上怎么做。

普及组贪心。按左端点排序,从 \(x=1\) 开始,每次选择左端点 \(\le x\) 的线段中右端点最右的,跳过去,重复这个过程。

再考虑环上。显然可以断环为链,长为 \(2n\),然后求每个长为 \(n\) 的区间的答案。

上面那个过程可以倍增优化。

时间复杂度 \(O(n\log n+m)\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2222222;
#define FOR(i,a,b) for(int i=(a);i<=(b);i++)
#define ROF(i,a,b) for(int i=(a);i>=(b);i--)
#define MEM(x,v) memset(x,v,sizeof(x))
inline ll read(){
	char ch=getchar();ll x=0,f=0;
	while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
	return f?-x:x;
}
int n,m,k,mx[maxn],to[22][maxn],ans=1e9;
int main(){
	n=read();m=read();k=2*n;
	FOR(i,1,k) mx[i]=i;
	FOR(i,1,m){
		int l=read(),r=read();
		if(l>r) mx[l]=max(mx[l],r+n+1);
		else mx[l]=max(mx[l],r+1),mx[l+n]=max(mx[l+n],r+n+1);
	}
	FOR(i,1,k) mx[i]=max(mx[i-1],mx[i]);
	FOR(i,1,k) to[0][i]=mx[i];
	FOR(j,1,21) FOR(i,0,k) to[j][i]=to[j-1][to[j-1][i]];
	FOR(i,1,n){
		int x=i,y=i+n,cnt=0;
		ROF(i,21,0) if(to[i][x] && to[i][x]<y) cnt+=1<<i,x=to[i][x];
		if(to[0][x]>=y) ans=min(ans,cnt+1);
	}
	if(ans==1e9) puts("impossible");
	else printf("%d\n",ans); 
}
posted @ 2020-10-15 20:53  ATS_nantf  阅读(179)  评论(0编辑  收藏  举报