传送门:CF1142
高质量的Div1
枚举起始点相距的整块数 i i i(基础距离为 i ⋅ k i·k i⋅k)
再 2 2 2^2 22枚举起始点分别位于整点前/后 a / b a/b a/b的情况。
取 n k gcd ( n k , l ) \dfrac{nk}{\gcd(nk,l)} gcd(nk,l)nk的 min \min min即可。
复杂度 O ( 4 n ) O(4n) O(4n)
#include
#define fi first
#define sc second
#define gc getchar
#define pb push_back
using namespace std;
const int N=2e5+10;
typedef long long ll;
typedef double db;
int n,k,a,b;
ll mn=1e18,mx,nw,len;
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
int main(){
int i,x,y;ll ix,iy;
scanf("%d%d%d%d",&n,&k,&a,&b);
len=(ll)n*k;
for(i=0;i<n;++i)
for(x=-1;x<2;x+=2)
for(y=-1;y<2;y+=2){
ix=x*a;iy=(ll)i*k+y*b;
nw=((iy-ix)%len+len)%len;
if(nw==0) nw+=len;
nw=len/__gcd(len,nw);
mx=max(nw,mx);mn=min(mn,nw);
}
printf("%I64d %I64d",mn,mx);
return 0;
}
Atcoder做多了,现在做DS真的菜到不行。
考虑没有 l i , r i l_i,r_i li,ri的限制:
设 f i f_i fi表示以 a i a_i ai结束的 a a a的子序列满足是 p p p的循环同构序列的最长长度,设 p r e i pre_i prei表示最大的 j j j满足 j < i , p k = a i , p ( k − 1 ) % n = a j j<i,p_k=a_i,p_{(k-1)\%n}=a_j j<i,pk=ai,p(k−1)%n=aj,显然 f i = f p r e i + 1 f_i=f_{pre_i}+1 fi=fprei+1
相当于 i → p r e i i\to pre_i i→prei连边,构成一个森林。倍增记录点 i i i的 2 k 2^k 2k级祖先。
维护 p o s i pos_i posi表示 i i i的第 n − 1 n-1 n−1个祖先(即最大的位置 p o s i pos_i posi满足 a a a的 [ p o s i , i ] [pos_i,i] [posi,i]区间中存在以 i i i结尾的 p p p的循环同构序列)。
( l i , r i ) (l_i,r_i) (li,ri)的询问相当于判断区间内 max ( p o s k ) ( k ∈ [ l i , r i ] ) \max(pos_k)(k\in[l_i,r_i]) max(posk)(k∈[li,ri])是否 ≥ l i \geq l_i ≥li。线段树维护即可。
(我竟然zz的没有想到这样套路的维护,然而动态删区间左端点,没法维护,还上莫队+set了,我怎么这么zz!)
#include
#define fi first
#define sc second
#define gc getchar
#define pb push_back
#define lc (k<<1)
#define rc (k<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=2e5+10;
typedef long long ll;
typedef double db;
int n,m,q,p[N],a[N],rv[N];
int tg[N],bel[N],pre[N],cnt,v[N],mx[N<<2];
int nt[N][20];
map<int,int>g[N];
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
void build(int k,int l,int r)
{
if(l==r) {mx[k]=v[l];return;}
build(lc,l,mid);build(rc,mid+1,r);
mx[k]=max(mx[lc],mx[rc]);
}
int ask(int k,int l,int r,int L,int R)
{
if(L<=l && r<=R) return mx[k];
int re=0;
if(L<=mid) re=ask(lc,l,mid,L,R);
if(R>mid) re=max(re,ask(rc,mid+1,r,L,R));
return re;
}
int main(){
int i,j,k,x,L,R;
rd(n);rd(m);rd(q);
for(i=0;i<n;++i) rd(p[i]);
for(i=1;i<=m;++i) rd(a[i]);
for(i=0;i<n;++i) rv[p[i]]=p[(i-1+n)%n];
for(i=1;i<=m;++i){
pre[a[i]]=i;x=pre[rv[a[i]]];nt[i][0]=x;
for(j=1;j<20;++j) nt[i][j]=nt[nt[i][j-1]][j-1];
for(L=n-2,j=0;(1<<j)<=L;++j) if((L>>j)&1) x=nt[x][j];
v[i]=x;
}
build(1,1,m);
for(;q;--q){
rd(L);rd(R);
if(ask(1,1,m,L,R)>=L) putchar('1');
else putchar('0');
}
return 0;
}
注意函数是 y = x 2 + b x + c y=x^2+bx+c y=x2+bx+c,没有 a a a!
( x , y ) → ( x , y − x 2 ) (x,y)\to (x,y-x^2) (x,y)→(x,y−x2),求上凸壳即可。
#include
#define fi first
#define sc second
#define gc getchar
#define pb push_back
#define lc (k<<1)
#define rc (k<<1|1)
#define mid ((l+r)>>1)
using namespace std;
const int N=1e5+10;
typedef long long ll;
typedef double db;
const db eps=1e-9;
int n,top;
struct P{
ll x,y;
bool operator <(const P&ky) const{
if(x!=ky.x) return x<ky.x;
return y<ky.y;
}
}p[N],stk[N];
char cp;
template<class T>inline void rd(T &x)
{
cp=gc();x=0;int f=0;
for(;!isdigit(cp);cp=gc()) if(cp=='-') f=1;
for(;isdigit(cp);cp=gc()) x=x*10+(cp^48);
if(f) x=-x;
}
inline double slope(P a,P b)
{
if(a.x==b.x) return 1e30;
return (double)(b.y-a.y)/(double)(b.x-a.x);
}
inline void convx()
{
int i,m=0;
sort(p+1,p+n+1);
for(i=1;i<=n;++i)
if((i==n && p[i].x!=p[i-1].x) || p[i].x!=p[i+1].x) p[m++]=p[i];
stk[(top=1)]=p[0];
for(i=1;i<m;++i){
for(;top>1 && slope(stk[top],p[i])+eps>=slope(stk[top-1],stk[top]);--top);
stk[++top]=p[i];
}
}
int main(){
int i,j,k,x,y;
rd(n);
for(i=1;i<=n;++i){
rd(x);rd(y);
p[i].y=y-(ll)x*x;p[i].x=x;
}
convx();
printf("%d",max(0,top-1));
return 0;
}
介绍一种 O ( N ⋅ 11 ) O(N·11) O(N⋅11)的方法:
归纳求出合法数 x x x的排名 r k x rk_x rkx(设 x x x的长度为 l e n len len):
d p [ i ] [ j ] dp[i][j] dp[i][j]表示处理到第 i i i位,前面可行解的排名 m o d 11 mod\ 11 mod 11的值为 j j j时最远能拓展到的位置。
详见代码
#include
using namespace std;
const int N=1e5+10;
typedef long long ll;
int n,f[N][12];
char s[N];ll ans;
int dp(int pos,int c)
{
if(~f[pos][c]) return f[pos][c];
if(pos>n) {f[pos][c]=n;return n;}
int nw=s[pos];
if(nw>=c) {f[pos][c]=pos-1;return pos-1;}
f[pos][c]=dp(pos+1,((c*(c-1)>>1)+nw+10)%11);
return f[pos][c];
}
int main(){
int i,j;
memset(f,0xff,sizeof(f));
scanf("%s",s+1);n=strlen(s+1);
for(i=1;i<=n;++i) s[i]-='0';
for(i=1;i<=n;++i) if(s[i])
ans+=dp(i+1,s[i])-i+1;
cout<<ans;
return 0;
}
考虑没有粉边限制时:
任意选一个点作为初始点 t o p top top,并记录它通过绿边可以到达的点集 S S S。
每次取任意一个它不能到达的点 y y y,判断 ( t o p , y ) (top,y) (top,y)连边方向:若是 t o p top top指向 y y y,将 y y y加入 S S S即可;否则将 y y y作为 t o p top top,并将之前的 t o p top top加入 S S S
显然 n n n次操作后 S = ∣ n − 1 ∣ S=|n-1| S=∣n−1∣。
考虑存在粉边限制时:
( t o p , y ) (top,y) (top,y)之间的连边可能是粉色的,不能保证路径上边颜色相同。
题解提供了一种构造方法:
将原图(只存在粉边)缩点,任选一个入度为 0 0 0的SCC作为 t o p top top,并任选其中一个点作为 t p tp tp。
如果只有这一个入度为 0 0 0的SCC,答案就是 t p tp tp。
否则任选另一个入度为0的SCC中的某个点 y y y,判断 ( t p , y ) (tp,y) (tp,y)连边方向。
假设 t p tp tp指向 y y y(否则 t o p top top转成这个SCC),相当于删去了 y y y这个点——如果 y y y所属SCC中所有点都被删完了,相当于删去了这个 S C C SCC SCC,同时删去它的出边,新生成一些入度为0的SCC。
显然最多 n − 1 n-1 n−1次后得到答案 t p tp tp
p.s
在删SCC点时,不需要考虑删点后SCC不强连通的情况(不然写起来很麻烦),只需要处理SCC点被删完的情况——相当于求出一条 t p tp tp出发的能走完一个SCC的绿边路径。
复杂度 O ( n + m ) O(n+m) O(n+m)
代码
#include
#define pb push_back
using namespace std;
const int N=1e5+10;
typedef long long ll;
int n,m,num,ind[N],bel[N];
map<int,int>mp[N];
vector<int>g[N],nt[N],st[N];
int q[N],tl;
int df[N],low[N],dfn,stk[N],top;
void tar(int x)
{
df[x]=low[x]=++dfn;stk[++top]=x;
for(int y:g[x]){
if(!df[y]){
tar(y);low[x]=min(low[x],low[y]);
}else if(!bel[y]) low[x]=min(low[x],df[y]);
}
if(low[x]==df[x]){
num++;
for(int tp;;){
tp=stk[top--];bel[tp]=num;
st[num].pb(tp);if(tp==x) break;
}
}
}
inline int ask(int x,int y)
{
cout<<"? "<<x<<" "<<y<<endl;
int re;cin>>re;
return re;
}
int main(){
int i,x,y,b;
cin>>n>>m;
for(i=1;i<=m;++i){
cin>>x>>y;g[x].pb(y);
}
for(i=1;i<=n;++i) if(!df[i]) tar(i);
for(i=1;i<=n;++i){
b=bel[i];
for(int y:g[i]) if(bel[y]!=b && (!mp[b][bel[y]])){
nt[b].pb(bel[y]);ind[bel[y]]++;mp[b][bel[y]]=1;
}
}
for(i=1;i<=num;++i) if(!ind[i]) q[++tl]=i;
for(;tl>1;){
x=st[q[1]].back();y=st[q[2]].back();
if(!ask(x,y)) swap(q[2],q[1]);
y=q[2];st[y].pop_back();
if(!st[y].size()){
swap(q[2],q[tl]);--tl;
for(int b:nt[y]){
ind[b]--;if(!ind[b]) q[++tl]=b;
}
}
}
cout<<"! "<<st[q[1]].back()<<endl;
return 0;
}