XVIII Open Cup named after E.V. Pankratiev. Grand Prix of Korea

A. Donut

扫描线+线段树。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=800010,M=2222222;
int n,m,cnt,i,j;ll L,R,D,a[N];
int tag[M],v[M],ans;
struct E{
	ll x,l,r;int s;
	E(){}
	E(ll _x,ll _l,ll _r,int _s){x=_x,l=_l,r=_r,s=_s;}
}e[N];
inline bool cmp(const E&a,const E&b){return a.x<b.x;}
inline void tag1(int x,int p){tag[x]+=p;v[x]+=p;}
void add(int x,int a,int b,int c,int d,int p){
	if(c<=a&&b<=d){
		tag1(x,p);
		return;
	}
	if(tag[x])tag1(x<<1,tag[x]),tag1(x<<1|1,tag[x]),tag[x]=0;
	int mid=(a+b)>>1;
	if(c<=mid)add(x<<1,a,mid,c,d,p);
	if(d>mid)add(x<<1|1,mid+1,b,c,d,p);
	v[x]=max(v[x<<1],v[x<<1|1]);
}
int main(){
	scanf("%d%lld%lld",&n,&L,&R);L--;
	while(n--){
		ll x,y;
		int s;
		scanf("%lld%lld%d",&x,&y,&s);
		e[++m]=E(x-R,y-R,y+R,s);
		e[++m]=E(x+R+1,y-R,y+R,-s);
		e[++m]=E(x-L,y-L,y+L,-s);
		e[++m]=E(x+L+1,y-L,y+L,s);
	}
	for(i=1;i<=m;i++)a[++cnt]=e[i].l,a[++cnt]=e[i].r;
	sort(a+1,a+cnt+1);
	sort(e+1,e+m+1,cmp);
	for(i=1;i<=m;i=j){
		for(j=i;j<=m&&e[i].x==e[j].x;j++)add(1,1,cnt,lower_bound(a+1,a+cnt+1,e[j].l)-a,lower_bound(a+1,a+cnt+1,e[j].r)-a,e[j].s);
		ans=max(ans,v[1]);
	}
	printf("%d",ans);
}

  

B. Circular Arrangement

留坑。

 

C. Earthquake

对于一条路径内部来说,最优策略肯定是从存在概率最小的开始询问。

对于不同路径之间来说,考虑排序不等式贪心即可。

#include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() {  }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = 1010, M = 0, Z = 1e9 + 7, inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int n;
double a[N];
long double win[N], fail[N];
struct A
{
	long double failp;
	long double step;
	bool operator < (const A & b) const
	{
		long double afirst = step + failp * b.step;
		long double bfirst = b.step + b.failp * step;
		return afirst < bfirst;
	}
}v[N];
int main()
{
	while(~scanf("%d",&n))
	{
		for(int i = 1; i <= n; ++i)
		{
			int g; scanf("%d", &g);
			for(int j = 1; j <= g; ++j)
			{
				scanf("%lf", &a[j]);
				a[j] /= 1000;
			}
			sort(a + 1, a + g + 1);
			win[0] = 1;
			fail[0] = 0;
			v[i].step = 0;
			for(int j = 1; j <= g; ++j)
			{
				win[j] = win[j - 1] * a[j];
				fail[j] = fail[j - 1] + win[j - 1] * (1 - a[j]);
				v[i].step += win[j - 1] * (1 - a[j]) * j;
			}
			v[i].step += win[g] * g;
			v[i].failp = fail[g];
		}
		sort(v + 1, v + n + 1);
		long double ans = 0;
		long double p = 1;
		for(int i = 1; i <= n; ++i)
		{
			ans += p * v[i].step;
			p *= v[i].failp;
		}
		printf("%.12f\n", (double)ans);
	}
	return 0;
}

/*
【trick&&吐槽】
2
3 900 900 900
2 100 100

3
1 240
1 310
1 50

【题意】


【分析】


【时间复杂度&&优化】


*/

  

D. Dynamic Input Tool

贪心,若不是子序列则进行一次操作。

#include<cstdio>
#include<cstring>
const int N=1000010;
int n,i,j,f[N][26];
int l,r,x,ans;
int vis[26];
char a[N];
int main(){
	scanf("%s",a+1);
	n=strlen(a+1);
	for(i=1;i<=n;i++)a[i]-='a';
	for(j=0;j<26;j++)f[n+1][j]=n+1;
	for(i=n;i;i--){
		for(j=0;j<26;j++)f[i][j]=f[i+1][j];
		f[i][a[i]]=i;
	}
	for(i=1;i<=n;i++){
		if(!vis[a[i]]){
			if(l&&l<i)ans++;
			vis[a[i]]=1;
			ans++;
			l=i+1;
			x=1;
		}else{
			x=f[x][a[i]];
			if(x>=l){
				ans++;
				x=f[1][a[i]];
				l=i;
			}
			x++;
		}
	}
	if(l<=n)ans++;
	printf("%d",ans);
}

  

E. Central Lake

答案即为圆周上距离最远的两个点的距离,求出两个点后可以求切线得出答案。

对于求最远点对,可以使用set支持插入操作,对于删除则按时间分治即可。

时间复杂度$O(n\log^2n)$。

#define ms(x, y) memset(x, y, sizeof(x))
#define mc(x, y) memcpy(x, y, sizeof(x))
#define mid (l+r>>1)
#define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r
#define ls o << 1
#define rs o << 1 | 1
#define rt 1,1,Q+1
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<queue>
#include<map>
#include<stack>
#include<vector>
#include<list>
#include<set>
#include<string>
#include<algorithm>
#pragma comment(linker,"/STACK:102400000,102400000")
template <class T> inline void gmax(T &a, T b){if(b > a) a = b;}
template <class T> inline void gmin(T &a, T b){if(b < a) a = b;}
using namespace std;
const int N = 4e5 + 10, M = 1e6 + 10, Z = 1e9 + 7, maxint = 2147483647, ms1 = 16843009, ms31 = 522133279, ms63 = 1061109567, ms127 = 2139062143;
const double PI = acos(-1.0), eps = 1e-8;
double R, r;
int n;
int LL, RR, X;
vector<int> w[N], v[N * 4];
int a[N];

/*
const int BS = 360000;
struct point{
    long double x, y, z;
    point(){}
    point (long double x_,long double y_,long double z_=1.0){x=x_,y=y_,z=z_;}
    point operator -(const point &p)const{
        return {x-p.x,y-p.y};
    }
    point operator +(const point &p)const{
        return {x+p.x,y+p.y};
    }
    point operator *(const long double &k)const{
        return {x*k,y*k};
    }
    long double Norm(){
        return sqrtl(x*x+y*y);
    }
    point Rotate(const long double &AA)const{
        long double c = cosl(AA);
        long double s = sinl(AA);
        return {c*x-s*y, x*s+c*y};
    }
}O;
point Seg(point a, point b){
    return {b.y-a.y,a.x-b.x,-a.x*b.y+a.y*b.x};
}
long double val(int d){
    long double AA = (long double)d/BS * 2*PI;
    point p = {(long double)R, (long double)0};
    point q = {R*cosl(AA), R*sinl(AA)};
    point S = Seg(p,q);
    long double dis = S.z/sqrtl(S.x*S.x+S.y*S.y);
    if(dis<0)dis=-dis;
    if(dis > r){
        return (q-p).Norm();
    }
    long double BB = asinl((long double)r/R);
    point v1 = (O-q).Rotate(BB);
    long double dd = sqrtl((long double)R*R-(long double)r*r);
    v1 = q + v1*((long double)1.0/v1.Norm()*dd);
    point v2 = (O-p).Rotate(-BB);
    v2 = p + v2*((long double)1.0/v2.Norm()*dd);
    return (q-v1).Norm() + (p-v2).Norm() + (atan2l(v1.y,v1.x) - atan2l(v2.y,v2.x))*r;
}
*/

struct Point
{
    double x, y;
    Point(double x_, double y_){ x = x_; y = y_; }
    friend Point operator - (const Point &a, const Point &b){
        return Point(a.x - b.x, a.y - b.y);
    }
    friend Point operator + (const Point &a, const Point &b){
        return Point(a.x + b.x, a.y + b.y);
    }
    friend Point operator * (const Point &a, const double &b){
        return Point(a.x * b, a.y * b);
    }
    friend Point operator / (const Point &a, const double &b){
        return Point(a.x / b, a.y / b);
    }
    double len2(){
        return x * x + y * y;
    }
    double len()
    {
        return sqrt(len2());
    }
    Point rotate(const double ang){
        return Point(cos(ang) * x - sin(ang) * y, cos(ang) * y + sin(ang) * x);
    }
    Point turn90(){
        return Point(-y, x);
    }
};
struct Circle
{
    Point o;
    double r;
    Circle(Point o = Point(0, 0), double r = 0) : o(o), r(r) {}
};

vector<Point> circleTangentPoint(const Circle &c, const Point &p0)
{
    double x = (p0 - c.o).len2(), r = c.r;
    double d = x - r * r;
    vector<Point> ret;
    if(d < -eps){
        return ret;
    }
    if(d < 0){
        d = 0;
    }
    Point p = (p0 - c.o) * (r * r / x), delta = ((p0 - c.o) * (-r * sqrt(d) / x)).turn90();
    ret.push_back(c.o + p + delta);
    ret.push_back(c.o + p - delta);
    return ret;
}

double TH(double x)
{
    return x * PI / 180;
}

int sgn(double x)
{
    if(fabs(x) < eps) return 0;
    return x > 0 ? 1 : -1;
}
double L(Point aa, Point bb)
{
    if(sgn((aa - bb).len2() - 4.0 * r * r) == 0){
        return PI * r;
    }
    Point cc = {0, 0};
    double a = (bb - cc).len(), b = (aa - cc).len(), c = (aa - bb).len();
    double cosc = (b * b + a * a - c * c) / (2 * a * b);
    double th = acos(cosc);
    return th * r;
}

double val(int n)
{
    //printf("%d\n", n);
    Point o = {0, 0};
    Point p = {0, -R}; Point pp = p;
    p = p.rotate(TH(1.0 * n / 1000));
    double dist = (pp - p).len();
    if(dist <= sqrt(R * R - r * r) * 2){
        return dist;
    }
    Circle c; c.o = {0, 0}; c.r = r;
    vector<Point> inter = circleTangentPoint(c, p);
    vector<Point> origi = circleTangentPoint(c, pp);
    Point p1 = inter[0], p2 = inter[1], p3 = origi[0], p4 = origi[1];
    double ans = min(min(L(p1, p3), L(p1, p4)), min(L(p2, p3), L(p2, p4)));
    return ans + 2 * (p - p1).len();
}

void build(int o, int l, int r)
{
    v[o].clear();
    if(l == r)return;
    build(lson);
    build(rson);
}
void update(int o, int l, int r)
{
    if(LL <= l && r <= RR)
    {
        v[o].push_back(X);
        return;
    }
    if(LL <= mid)update(lson);
    if(RR > mid)update(rson);
}
set<int>sot;
const int A = 360000;
const int H = 180000;
double ans[N];
int minn(int x)
{
    if(x<0)x=-x;
    x %= A;
    x += A;
    x %= A;
    return min(x, A - x);
}
void solve(int o, int l, int r, int maxD)
{
    for(auto x : v[o])
    {
        set<int>::iterator it = sot.lower_bound(x + H);
        if(it != sot.end())gmax(maxD, minn(x + A - *it));
        if(it != sot.begin())gmax(maxD, minn(*(--it) - x));
        sot.insert(x);
        sot.insert(x + A);
    }
    if(l == r)
    {
        ans[l] = val(maxD);
    }
    else
    {
        solve(lson, maxD);
        solve(rson, maxD);
    }
    for(auto x : v[o])
    {
        sot.erase(x);
        sot.erase(x + A);
    }
    v[o].clear();
}
int main()
{
    while(~scanf("%lf%lf", &R, &r))
    {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
        {
            scanf("%d", &a[i]);
            w[a[i]].push_back(1);
        }
        int Q;
        scanf("%d", &Q);
        build(rt);
        for(int i = 1; i <= Q; ++i)
        {
            int op, x;
            scanf("%d%d", &op, &x);
            w[x].push_back(i + 1);
        }
        for(int i = 0; i < 360000; ++i)
        {
            X = i;
            for(int j = 0; j < w[i].size(); j += 2)
            {
                LL = w[i][j];
                if(j == w[i].size() - 1)RR = Q + 1;
                else RR = w[i][j + 1] - 1;
                update(rt);
            }
            w[i].clear();
        }
        solve(rt, 0);
        for(int i = 1; i <= Q + 1; ++i)printf("%.10f\n", ans[i]);
    }
    return 0;
}

/*

 题意:
10 5
2
0 90000
4
1 180000
1 240000
2 0
2 90000


 类型:

 分析:

 优化:

 trick:

 数据:

 Sample Input

 Sample Output


 */

  

F. Computing MDSST

设$f[S][i]$表示$S$集合的点形成一棵树,根为$i$的最小代价,枚举补集的子集转移。

时间复杂度$O(3^nn^2)$。

#include<cstdio>
typedef long long ll;
const int N=15;
const ll inf=1LL<<60;
int n,i,j,S,U,T,c[1<<N],g[N][N];ll f[1<<N][N];
inline void up(ll&a,ll b){a>b?(a=b):0;}
int main(){
	scanf("%d",&n);
	for(i=0;i<n;i++)for(j=i+1;j<n;j++){
		scanf("%d",&g[i][j]);
		g[j][i]=g[i][j];
	}
	for(S=0;S<1<<n;S++)for(i=0;i<n;i++)f[S][i]=inf;
	for(i=0;i<1<<n;i++)c[i]=__builtin_popcount(i);
	for(i=0;i<n;i++)f[1<<i][i]=0;
	for(S=0;S<1<<n;S++)for(i=0;i<n;i++)if(S>>i&1){
		for(j=0;j<n;j++)if(j!=i&&(S>>j&1)){
			T=S^(1<<i)^(1<<j);
			for(U=T;;U=(U-1)&T){
				up(f[S][i],f[(T-U)|(1<<i)][i]+f[U|(1<<j)][j]+1LL*g[i][j]*(c[U]+1)*(n-c[U]-1));
				if(!U)break;
			}
		}
	}
	for(i=1;i<n;i++)up(f[(1<<n)-1][0],f[(1<<n)-1][i]);
	printf("%lld",f[(1<<n)-1][0]);
}

  

G. MST with Metropolis

首先求出最小生成树,那么对于每个点可以看成加入$deg_i$条边权为$0$的边,LCT维护即可。

时间复杂度$O(m\log m)$。

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1000000;
struct edge{int x,y,a;}e[N];
bool cmp(const edge&a,const edge&b){return a.a<b.a;}
int n,m,i,j,fa[N];
int f[N],son[N][2],val[N],sum[N],from[N],tmp[N];bool rev[N];
vector<int>vg[N];
ll all;
int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);}
inline bool isroot(int x){return !f[x]||(son[f[x]][0]!=x&&son[f[x]][1]!=x);}
inline void rev1(int x){if(!x)return;swap(son[x][0],son[x][1]);rev[x]^=1;}
inline void pb(int x){if(rev[x])rev1(son[x][0]),rev1(son[x][1]),rev[x]=0;}
inline void up(int x){
  sum[x]=val[x],from[x]=x;
  if(son[x][0])if(sum[son[x][0]]>sum[x])sum[x]=sum[son[x][0]],from[x]=from[son[x][0]];
  if(son[x][1])if(sum[son[x][1]]>sum[x])sum[x]=sum[son[x][1]],from[x]=from[son[x][1]];
}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][w^1];
  if(son[x][w^1])f[son[x][w^1]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;
    if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];f[y]=x;son[x][w^1]=y;up(y);
}
inline void splay(int x){
  int s=1,i=x,y;tmp[1]=i;
  while(!isroot(i))tmp[++s]=i=f[i];
  while(s)pb(tmp[s--]);
  while(!isroot(x)){
    y=f[x];
    if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  up(x);
}
inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
inline void makeroot(int x){access(x);splay(x);rev1(x);}
inline void link(int x,int y){makeroot(x);f[x]=y;access(x);}
inline void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;up(x);}
inline void cut(int x,int y){makeroot(x);cutf(y);}
inline int askfrom(int x,int y){makeroot(x);access(y);splay(y);return from[y];}
inline ll solve(int x){
  ll sum=0;
  vector<int>v;
  for(int i=0;i<vg[x].size();i++){
    int k=vg[x][i];
    sum+=e[k].a;
    int A=x,B=e[k].x+e[k].y-A;
    int j=askfrom(A,B);
    sum-=e[j-n].a;
    v.push_back(j);
    cut(j,e[j-n].x),cut(j,e[j-n].y);
    link(A,n+m+k),link(B,n+m+k);
  }
  for(int i=0;i<vg[x].size();i++){
    int k=vg[x][i];
    int A=x,B=e[k].x+e[k].y-A;
    cut(A,n+m+k),cut(B,n+m+k);
  }
  for(int i=0;i<v.size();i++){
    int j=v[i];
    link(j,e[j-n].x),link(j,e[j-n].y);
  }
  return sum+all;
}
int main(){
  scanf("%d%d",&n,&m);
  for(i=1;i<=m;i++)scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].a);
  sort(e+1,e+m+1,cmp);
  for(i=1;i<=n;i++)fa[i]=i,val[i]=-1,from[i]=i;
  for(i=1;i<=m;i++)val[n+i]=e[i].a,from[n+i]=n+i;
  for(i=1;i<=m;i++)val[n+m+i]=0,from[n+m+i]=n+m+i;
  for(i=1;i<=m;i++)if(F(e[i].x)!=F(e[i].y)){
    fa[fa[e[i].x]]=fa[e[i].y];
    link(e[i].x,n+i);
    link(e[i].y,n+i);
    all+=e[i].a;
  }
  for(i=1;i<=m;i++)vg[e[i].x].push_back(i),vg[e[i].y].push_back(i);
  for(i=1;i<=n;i++)printf("%lld\n",solve(i));
}

  

H. Number of Cycles

留坑。

 

I. Sum of Squares of the Occurrence Counts

建立后缀自动机,设$w_i=ml_i-ml_{pre_i},v_i$表示$i$点的$right$集合大小,则$ans=\sum w_iv_i^2$。

每次加入一个新点时,需要将该点到根路径上所有$v$值加$1$,LCT打标记维护即可。

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

#include<cstdio>
#include<cstring>
#define N 200010
typedef long long ll;
char s[N];int n,i;ll ans;
int tot=1,last=1,pre[N],son[N][26],ml[N];
namespace LCT{
int f[N],son[N][2],a[N];
ll tag[N];
bool rev[N];
ll w[N],v[N],sw[N],swv[N];
inline void swap(int&a,int&b){int c=a;a=b;b=c;}
inline bool isroot(int x){return !f[x]||son[f[x]][0]!=x&&son[f[x]][1]!=x;}
inline void reverse(int x){if(x)swap(son[x][0],son[x][1]),rev[x]^=1;}
inline void add(int x,ll y){
  if(x){
    v[x]+=y;
    swv[x]+=sw[x]*y;
    tag[x]+=y;
  }
}
inline void up(int x){
  sw[x]=w[x]+sw[son[x][0]]+sw[son[x][1]];
  swv[x]=w[x]*v[x]+swv[son[x][0]]+swv[son[x][1]];
}
inline void pb(int x){
  if(rev[x])reverse(son[x][0]),reverse(son[x][1]),rev[x]=0;
  if(tag[x])add(son[x][0],tag[x]),add(son[x][1],tag[x]),tag[x]=0;
}
inline void rotate(int x){
  int y=f[x],w=son[y][1]==x;
  son[y][w]=son[x][w^1];
  if(son[x][w^1])f[son[x][w^1]]=y;
  if(f[y]){
    int z=f[y];
    if(son[z][0]==y)son[z][0]=x;else if(son[z][1]==y)son[z][1]=x;
  }
  f[x]=f[y];son[x][w^1]=y;f[y]=x;up(y);
}
inline void splay(int x){
  int s=1,i=x,y;a[1]=i;
  while(!isroot(i))a[++s]=i=f[i];
  while(s)pb(a[s--]);
  while(!isroot(x)){
    y=f[x];
    if(!isroot(y)){if((son[f[y]][0]==y)^(son[y][0]==x))rotate(x);else rotate(y);}
    rotate(x);
  }
  up(x);
}
inline void access(int x){for(int y=0;x;y=x,x=f[x])splay(x),son[x][1]=y,up(x);}
inline void makeroot(int x){access(x);splay(x);reverse(x);}
inline void link(int x,int y){makeroot(x);f[x]=y;access(x);}
inline void cutf(int x){access(x);splay(x);f[son[x][0]]=0;son[x][0]=0;}
inline void cut(int x,int y){makeroot(x);cutf(y);}
inline void changew(int x,ll p){
  access(x);
  splay(x);
  w[x]=p;
  up(x);
}
inline void maketag(int x){
  makeroot(1);
  access(x);
  splay(x);
  ans+=2*swv[x]+sw[x];
  add(x,1);
}
inline void copy(int y,int x){
  access(x);splay(x);
  w[y]=w[x];
  v[y]=v[x];
  up(y);
}
inline void setv(int x,int p){
  w[x]=p;
  up(x);
}
}
inline void add(int w){
  int p=++tot,x=last,r,q;
  ml[p]=ml[x]+1;last=p;
  for(;x&&!son[x][w];x=pre[x])son[x][w]=p;
  if(!x){
    pre[p]=1;
    LCT::setv(p,ml[p]-ml[pre[p]]);
    LCT::link(p,pre[p]);
  }else if(ml[x]+1==ml[q=son[x][w]]){
    pre[p]=q;
    LCT::setv(p,ml[p]-ml[pre[p]]);
    LCT::link(p,pre[p]);
  }else{
    pre[r=++tot]=pre[q];
    LCT::copy(r,q);
    LCT::link(r,pre[q]);
    memcpy(son[r],son[q],sizeof son[r]);
    ml[r]=ml[x]+1;
    LCT::cut(q,pre[q]);
    LCT::link(q,r);
    pre[p]=pre[q]=r;
    LCT::setv(p,ml[p]-ml[pre[p]]);
    LCT::link(p,r);
    LCT::changew(r,ml[r]-ml[pre[r]]);
    LCT::changew(q,ml[q]-ml[pre[q]]);
    for(;x&&son[x][w]==q;x=pre[x])son[x][w]=r;
  }
  LCT::maketag(p);
}
int main(){
  scanf("%s",s+1);n=strlen(s+1);
  for(i=1;i<=n;i++){
    add(s[i]-'a');
    printf("%lld\n",ans);
  }
}

  

J. Game of Sorting

对于一个区间$[l,r]$,若它是单调的,则显然先手必败。

若去掉$l$或者$r$后变成了单调的,则显然先手必胜。

若$[l+1,r-1]$是单调的,那么同理先手必败。

若$[l,r-2]$是单调的,那么先手若取$r$会导致后手必胜,后手同理,故两人会一直取$l$直到出现上面第一种或者第二种情况,可以根据奇偶性判断。

同理可以得出$[l+2,r]$是单调的情形的处理方法。

除此之外,有$[l,r]$的答案等于$[l+1,r-1]$的答案,二分找到最小的$k$满足$[l+k,r-k]$可以由上面方法直接判断出胜负即可。

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

#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1000010;
int n,i,a[N],fl[N],fr[N],gl[N],gr[N],m,x,y;
inline int check(int l,int r){
  if(l>=r)return -1;
  if(fr[l]>=r)return -1;
  if(fr[l]==r-1)return 1;
  if(fl[r]==l+1)return 1;
  if(fr[l+1]==r-1)return -1;
  int x;
  if(fr[l]==r-2){
    if(fl[r-1]<fl[r])x=(l&1)^(fl[r-1]&1)^1;
    else x=(l&1)^(fl[r]&1);
    return x?1:-1;
  }
  if(fl[r]==l+2){
    if(fr[l+1]>fr[l])x=(r&1)^(fr[l+1]&1)^1;
    else x=(r&1)^(fr[l]&1);
    return x?1:-1;
  }
  return 0;
}
inline int cal(int x,int y){
  int l=0,r=(y-x)/2+5,mid,t,o;
  while(l<=r){
    mid=(l+r)>>1;
    t=check(x+mid,y-mid);
    if(!t)l=mid+1;else r=mid-1,o=t;
  }
  return o;
}
int main(){
  scanf("%d",&n);
  for(i=1;i<=n;i++)scanf("%d",&a[i]);
  for(fr[n]=gr[n]=n,i=n-1;i;i--){
    fr[i]=a[i]<=a[i+1]?fr[i+1]:i;
    gr[i]=a[i]>=a[i+1]?gr[i+1]:i;
  }
  for(fl[1]=gl[1]=1,i=2;i<=n;i++){
    fl[i]=a[i]<=a[i-1]?fl[i-1]:i;
    gl[i]=a[i]>=a[i-1]?gl[i-1]:i;
  }
  for(i=1;i<=n;i++)fl[i]=min(fl[i],gl[i]),fr[i]=max(fr[i],gr[i]);
  scanf("%d",&m);
  while(m--)scanf("%d%d",&x,&y),puts(cal(x,y)>0?"Alice":"Bob");
}

  

K. Subsequence Queries

设$f_i$表示以字符$i$为结尾的本质不同子序列的个数,$sum$为所有本质不同子序列(包含空串)的个数,则一开始$f_i=0,sum=1$。

若往序列末尾添加一个字符$x$,则有$f_x=sum,sum=2sum-f_x$,可以将其写成转移矩阵$G_x$。

考虑将上述式子倒过来,则可以得出删除末尾字符$x$的转移,也就是$G_x^{-1}$。

则区间$[l,r]$的答案$=G_rG_{r-1}...G_{l+1}G_l=G_{r+1}^{-1}G_{r+2}^{-1}G_{n-1}^{-1}G_n^{-1}G_nG_{n-1}...G_{l+1}G_l$。

故预处理出$G$的后缀积的最后一列以及$G^{-1}$的后缀积的最后一行即可在$O(S)$的时间里求出$sum$。

对于预处理,注意到$G$和$G^{-1}$与单位矩阵$E$相比只有$4$个位置不同,故可以只考虑这$4$个位置的线性贡献在$O(S)$的时间里完成矩阵乘法。

时间复杂度$O((n+q)S)$。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define rep(i) for(int i=0;i<M;i++)
using namespace std;
const int N=1000010,M=53,P=1000000007;
int n,i,j,g[M][M],w[M][M],tmp[M],b[N][M],c[N][M];char a[N];
inline int getid(char x){
	if(x>='a'&&x<='z')return x-'a';
	return x-'A'+26;
}
inline void apply(int x){
	//x,x,-1
	//x,M-1,1
	//M-1,x,-1
	//M-1,M-1 1
	rep(i)w[i][x]=g[i][x],w[i][M-1]=g[i][M-1];
	rep(i){
	 	(g[i][x]-=w[i][x])%=P;
	 	(g[i][M-1]+=w[i][x])%=P;
	 	(g[i][x]-=w[i][M-1])%=P;
	 	(g[i][M-1]+=w[i][M-1])%=P;
	}
}
inline void applyinv(int x){
	rep(i)tmp[i]=g[x][i],g[x][i]=(2LL*g[x][i]-g[M-1][i])%P;
	rep(i)g[M-1][i]=tmp[i];
	/*newf[x]=olds
	news=olds*2-oldf[x]
	
	olds=newf[x]
	oldf[x]=newf[x]*2-news*/
}
inline int cal(int l,int r){
	int ret=0;
	rep(i)ret=(1LL*b[l][i]*c[r+1][i]+ret)%P;
	return (ret+P)%P;
}
const int Base=1000000000;
int len[M+1];
int num[M+1][N];
inline void copy(int x,int y){
	len[x]=len[y];
	for(int i=len[x];i;i--)num[x][i]=num[y][i];
}
inline void mul2(int x){
	int l=len[x],i;
	num[x][++l]=0;
	for(i=1;i<=l;i++)num[x][i]<<=1;
	for(i=1;i<l;i++)if(num[x][i]>=Base)num[x][i+1]++,num[x][i]-=Base;
	while(l>1&&!num[x][l])l--;
	len[x]=l;
}
inline void sub(int x,int y){
	int l=len[x],i;
	for(i=len[y];i;i--)num[x][i]-=num[y][i];
	for(i=1;i<l;i++)if(num[x][i]<0)num[x][i+1]--,num[x][i]+=Base;
	while(l>1&&!num[x][l])l--;
	len[x]=l;
}
inline void gao(int x){
	copy(M,x);
	copy(x,M-1);
	mul2(M-1);
	sub(M-1,M);
}
void write(int x){
	printf("%d",num[x][len[x]]);
	for(int i=len[x]-1;i;i--)printf("%09d",num[x][i]);
}
int main(){
	scanf("%s",a+1);
	n=strlen(a+1);
	rep(i)g[i][i]=1;
	rep(j)b[n+1][j]=g[j][M-1];
	for(i=n;i;i--){
		apply(getid(a[i]));
		rep(j)b[i][j]=g[j][M-1];
	}
	rep(i)rep(j)g[i][j]=0;
	rep(i)g[i][i]=1;
	rep(j)c[n+1][j]=g[M-1][j];
	for(i=n;i;i--){
		applyinv(getid(a[i]));
		rep(j)c[i][j]=g[M-1][j];
	}
	/*int x,y;
	while(~scanf("%d%d",&x,&y)){
		printf("%d\n",cal(x,y));
	}*/
	int q,a0,b0,pp,qq,rr,last=0;
	int L,R;
	scanf("%d%d%d%d%d%d",&q,&a0,&b0,&pp,&qq,&rr);
	while(q--){
		int na=(1LL*pp*a0+1LL*qq*b0+last+rr)%P;
		int nb=(1LL*pp*b0+1LL*qq*a0+last+rr)%P;
		int x=min(na%n+1,nb%n+1);
		int y=max(na%n+1,nb%n+1);
		L=x,R=y;
		last=cal(x,y);
		a0=na,b0=nb;
	}
	printf("%d",last);
	return 0;
	rep(i)len[i]=1;
	num[M-1][1]=1;
	for(i=L;i<=R;i++)gao(getid(a[i]));
	write(M-1);
}
/*
aadhuihGDSTYFSJHJUYFRgdgjesfgAGFDHSFHDTedsjydaJdgHSdfhyjFhsGvfhdzgfjsfjySfgyjhzdfgjzdfgjzfgjhdfgjGfyjhzdgfzjyfgzdjhgfdjdfdfdfdfdfdfdfdfdffdfdfzgjSGHFDsyJHFGhGFujyFGTjySFyJGDjSdgjyhSFhgZFGjhSDF
100 100 245 463 356 234243
85192723079788

*/

  

L. XOR Transformation

将$T$二进制拆分,将每个$1$拿出来单独做$2^t$步操作。

对于$2^t$步操作,每个点的值会变成往后$2^t$一步$k$个点的异或和,将$k$模以两倍的环长后用前缀异或和计算即可。

时间复杂度$O(n\log T)$。

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=222222;
int n,m,i,a[N];ll T;
int v[N],q[N],b[N];
void solve(int k){
	ll t=1LL<<k;
	t%=n;
	int i,j;
	for(i=0;i<n;i++)v[i]=0;
	for(i=0;i<n;i++)if(!v[i]){
		int len=0;
		for(j=i;!v[j];j=(j+t)%n)v[q[len++]=j]=1;
		for(j=0;j<len;j++)b[j]=b[j+len]=a[q[j]];
		for(j=1;j<len+len;j++)b[j]^=b[j-1];
		for(j=0;j<len;j++){
			int val=b[((j+m-1)%(len*2)+len*2)%(len*2)];
			if(j)val^=b[j-1];
			a[q[j]]=val;
		}
	}
}
int main(){
	scanf("%d%d%lld",&n,&m,&T);
	for(i=0;i<n;i++)scanf("%d",&a[i]);
	for(i=0;i<62;i++)if(T>>i&1)solve(i);
	for(i=0;i<n;i++)printf("%d ",a[i]);
}

  

posted @ 2018-04-03 22:36  Claris  阅读(1253)  评论(0编辑  收藏  举报