缇翾唐闸

ABC315 Ex

就当半在线卷积学习笔记了🙃

首先如果已经求得\(F[l,mid],G[l,mid]\)

这里先假设\(f_0\)有值吧

如果我们直接让\(F\)\(G[0,r-l+1)\),这样会出现\(r-l+1>mid\)的情况

不过可以发现,如果长度为\(2\)的次幂,这样的情况只会在\(l=0\)的情况出现

然后分桃

\(l=0\),我们让\(F(0,mid),G(0,mid)\)卷起来,这样只会出现\(F(0,mid)\)\(G[mid,r)\)的情况没考虑到

\(l\not=0\),正常的是\(F[l,mid)\)\(G[0,r-l+1)\),由于前面\(l=0\)的情况有一部分没算到,我们在加上\(G[l,mid)\)\(F[0,r-l+1)\)

对于这个题,我们只用求\(g_i=\sum\limits_{j=0}^{i-1}f_j\),同样这里分治一下

注意这个前缀和是\(i-1\)

Show Code
#include<bits/stdc++.h>
#define eps 1e-5
using namespace std;
const int MAXN=1e6+5;
const int MOD=998244353;
const int g=3;
int Pow(int a,int b,int p)
{
	int res=1;
	int base=a;
	while(b)
	{
		if(b&1)
		{
			res=((long long)res*base)%p;
		}
		base=((long long)base*base)%p;
		b>>=1;
	}
	return res;
}
int inv(int a,int p)
{
	return Pow(a,p-2,p);
}
int Rev[MAXN*4];
struct Poly{
	vector<int>U;
	void NTT(int Limit,int type)
	{
		int Len=(1<<Limit);
		for(int i=0;i<Len;i++)
		{
			Rev[i]=((Rev[i>>1]>>1)|((i&1)<<(Limit-1)));
		}
		
		while(U.size()<Len)
		{
			U.push_back(0);
		}
		for(int i=0;i<Len;i++)
		{
			if(i<Rev[i])
			{
				swap(U[i],U[Rev[i]]);
			}
		}
		for(int l=1;l<Len;l<<=1)
		{
			int Wn=Pow(g,(MOD-1)/(l<<1),MOD);
			if(type==-1)
			{
				Wn=inv(Wn,MOD);
			}
			for(int i=0;i<Len;i+=(l<<1))
			{
				int W=1;
				for(int j=i;j<i+l;j++,W=((long long)W*Wn)%MOD)
				{
					int Xc=U[j];
					int Yc=((long long)U[j+l]*W)%MOD;
					U[j]=((long long)Xc+Yc)%MOD;
					U[j+l]=((long long)Xc-Yc+MOD)%MOD;
				}
			}
		}
		if(type==-1)
		{
			int Liv=inv(Len,MOD); 
			for(int i=0;i<Len;i++)
			{
				U[i]=((long long)U[i]*Liv)%MOD;	
			}
		}
	}
	
}A,B;
Poly Mul_NTT(Poly A,Poly B)
{
	int N=A.U.size();
	int M=B.U.size();
	int nox=1;
	int Lm=0;
	while(nox<=(N+M-2))
	{
		nox<<=1;
		Lm++;
	 } 
	 A.NTT(Lm,1);
	 B.NTT(Lm,1);
	 for(int i=0;i<nox;i++)
	 {
	 	A.U[i]=((long long)A.U[i]*B.U[i])%MOD;
	 }
	 A.NTT(Lm,-1);
	 while(A.U.size()>(N+M-1))
	 {
	 	A.U.pop_back();
	 }
	 return A;
}
int n;
int G[MAXN];
int F[MAXN];
int a[MAXN];
void solve(int l,int r)
{
    if(l+1==r)
    {
        if(l>1)
        {
            G[l]=((long long)G[l-1]+G[l])%MOD;
            F[l]=((long long)G[l]*a[l])%MOD;
        }
        return;
    }
    int mid=(l+r)>>1;
    solve(l,mid);
    if(l==0)
    {
        A.U.resize(mid+1,0);
        for(int i=0;i<mid;i++)
        {
            A.U[i]=F[i];
        }
        A=Mul_NTT(A,A);
        for(int i=mid;i<r;i++)
        {
            G[i]=((long long)G[i]+A.U[i])%MOD;
        }
    }   
    else
    {
        Poly A,B;
        A.U.clear();
        B.U.clear();
        for(int i=l;i<r;i++)
        {
            A.U.push_back(F[i-l]);
            if(i<mid)
            {
                B.U.push_back(F[i]);
            }
        }
        A=Mul_NTT(A,B);
        for(int i=mid;i<r;i++)
        {
            G[i]=((long long)G[i]+2ll*A.U[i-l])%MOD;
        }
    }
    solve(mid,r);
}
signed main()
{   
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=2;i<=n+1;i++)
    {
        scanf("%d",&a[i]);
    }
    int N=n;
    int Lit=1;
    int Nox=0;
    while(Lit<=n+1)
    {
        Lit=(Lit<<1);
        Nox++;
    }
    n=Lit;
    F[1]=1;
    solve(0,n);
    for(int i=2;i<=N+1;i++)
    {
        printf("%d ",F[i]);
    }
}       

CF848D Shake It!

甚至连第一步都没想到

先把最小割转化成最大流

考虑第一次操作产生点\(u\),路径为\(S\rightarrow u\rightarrow T\)

如果后面的操作没有选到\((S,T)\),那最大流就是\(S\rightarrow u,u\rightarrow T\)的两个子问题最大流中小的那个\(+1\),就相当于是串联一下

考虑我们\(S\rightarrow T\)的最大流实际上就是由若干个\(u\)并联组合起来

这个并联和串联一起计数好像不好弄

考虑\(f_{i,j}\)表示\(i\)次操作,最大流为\(j\)\(S\rightarrow T\)的方案数

\(g_{i,j}\)表示\(i\)次操作,最大流为\(j\)\(S\rightarrow u\rightarrow T\)的方案数且后面不能操作\((S,T)\)的方案数

\(f\rightarrow g\)的转移不难写出,\(g_{i,j}=\sum\limits_{a+b=i-1}\sum\limits_{min(p,q)=j}f_{a,p}f_{b,q}\)

这玩意用个后缀优化一下

\(g\rightarrow f\)是个完全背包,就是把\((i,j,g_{i,j})\)看成一个物品,不过这里相同物品转移的时候注意要去重,因为两个物品合并时两个相同的方案之间应该没有顺序,这里我们直接枚举选了\(k\)次,这\(k\)次要分到\(g_{i,j}\)中的一个

这里把\(i\)作为阶段,每次可以求出\(g_{i,j}\)并直接拿去算背包

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=998244853;
const int MAXN=105;
int Pow(int a,int b,int p)
{
	int res=1;
	int base=a;
	while(b)
	{
		if(b&1)
		{
			res=((long long)res*base)%p;
		}
		base=((long long)base*base)%p;
		b>>=1;
	}
	return res;
}
int inv(int a,int p)
{
	return Pow(a,p-2,p);
}
int n,m;
int f[MAXN][MAXN];
int g[MAXN][MAXN];
int Sf[MAXN][MAXN];
int Sg[MAXN][MAXN];
int h[MAXN][MAXN];
int Inv[MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        Inv[i]=inv(i,MOD);
    }
    f[0][1]=1;
    h[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=n+1;j>=1;j--)
        {
            Sf[i-1][j]=((long long)Sf[i-1][j+1]+f[i-1][j])%MOD;
        }
        for(int j=n+1;j>=1;j--)
        {
            for(int k=0;k<=i-1;k++)
            {
                Sg[i][j]=((long long)Sg[i][j]+((long long)Sf[i-1-k][j]*Sf[k][j])%MOD)%MOD;
            }
            g[i][j]=((long long)Sg[i][j]-Sg[i][j+1]+MOD)%MOD;
        }

        for(int j=1;j<=n+1;j++)
        {
            for(int x=n;x>=0;x--)
            {
                for(int y=n+1;y>=0;y--)
                {
                    int now=g[i][j];
                    for(int k=1;x-i*k>=0&&y-(j*k)>=0;k++)
                    {
                        h[x][y]=(((long long)h[x-i*k][y-(j*k)]*now)%MOD+h[x][y])%MOD;
                        now=((long long)now*Inv[k+1])%MOD;
                        now=((long long)now*(g[i][j]+k))%MOD;
                    }
                }
            }
        }

        for(int j=1;j<=n+1;j++)
        {
            f[i][j]=h[i][j-1];
        }
    }
    printf("%d\n",f[n][m+1]);
}

ABC310Ex Negative Cost

\(nb\)性质题

翻译的官方题解???

\(L=300\)

首先存在我们选得招式的一种的前缀和不可能超过\(2L\),原因很简单,如果超过\(2L\)我们就选\(>0\)的肯定不劣

然后对于一个合法的方案可以有若干个合法方案拼接起来,存在一种划分方案使得每种方案选的个数\(\le2L\)

证明似乎是考虑找一个划分点为前面都是加魔力值后面是减,这样的点肯定是每\(2L\)个至少出现一次

因此我们可以用\(dp\)求出选\(i\)个招式能造成的最大伤害\(D_i\),然后考虑把他们拼起来

我们设\(D_i/i\)最大的\(i\)\(K\)

可以证明除\(K\)以外每个\(i\)最多选\(2L\)次,大概是因为我们尽量是要凑出\(H\bmod D_K\),这个用其他数来凑,然后在用\(K\)

然后再跑个完全背包即可

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=305;
int n;
long long H;
int c[MAXN];
long long d[MAXN];
long long f[MAXN*2][MAXN*2];
long long D[MAXN*2];
long long g[(MAXN*2)*(MAXN*2)];
signed main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %lld",&n,&H);
    int L=300;
    for(int i=1;i<=n;i++)
    {
        scanf("%d %lld",&c[i],&d[i]);
    }
    memset(f,-0x3f,sizeof(f));
    f[0][0]=0;
    for(int i=0;i<2*L;i++)
    {
        for(int j=0;j<=2*L;j++)
        {
            for(int k=1;k<=n;k++)
            {
                int To=j-c[k];
                if(To>=0)
                {
                    f[i+1][To]=max(f[i+1][To],f[i][j]+d[k]);
                }
            }
        }
    }
    double Maxi=0;
    int Key;
    for(int i=1;i<=2*L;i++)
    {
        D[i]=-0x3f3f3f3f3f3f3f3f;
        for(int j=0;j<=2*L;j++)
        {
            D[i]=max(D[i],f[i][j]);
        }
        if((D[i]*1.0/i)>Maxi)
        {
            Maxi=(D[i]*1.0/i);
            Key=i;
        }
    }
    //printf("%d %lld??\n",Key,D[Key]);
    memset(g,-0x3f,sizeof(g));
    g[0]=0;
    for(int i=1;i<=2*L;i++)
    {
        for(int j=i;j<=(2*L)*(2*L);j++)
        {
            g[j]=max(g[j],g[j-i]+D[i]);
        }
    }
    long long Res=1e18;

    for(int i=0;i<=(2*L)*(2*L);i++)
    {
        long long Rest=max(H-g[i],0ll);
        long long Ned;
        if(Rest%D[Key])
        {
            Ned=((Rest/D[Key])+1)*Key;
        }
        else
        {
            Ned=((Rest/D[Key]))*Key;
        }
        Res=min(Res,Ned+i);
    }
    printf("%lld\n",Res);
    return 0;
}

CF735E

可能要成智障了/kk

一个不难想到得思路是设\(dp_{x,i,j}\)表示在\(x\)中,子树内最近染色点距离为\(i\),子树外最近为\(j\)的方案数

不过这样的话要考虑很多东西,而且细节颇多(被题解搏杀了/kk

考虑换一种思路,\(dp_{x,i,j}\)表示表示在\(x\)中,子树内最近染色点距离为\(i\)而子树内最远未被满足点的需求为\(j\)的方案数,这玩意转移起来可能就方便,可能就不用考虑\(k+1\)的点了

而实际上仔细想想就可以发现\(i,j\)无法共存,因为如果\(i+j>k\)的话如果\(i\)不染色就一定就不上了,所以只用一维就够了

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=105;
int n,k;
int x,y;
vector<int>g[MAXN];
int dp[MAXN][55];
int Tmp[55];
void dfs(int x,int f)
{
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        dfs(v,x);
    }
    dp[x][k+1]=dp[x][0]=1;
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        for(int p=0;p<=2*k;p++)
        {
            Tmp[p]=dp[x][p];
            dp[x][p]=0;
        }
        for(int p=0;p<=2*k;p++)
        {
            for(int q=0;q<=2*k;q++)
            {
                if(p+q<=2*k)
                {
                    dp[x][min(p,q+1)]=((long long)dp[x][min(p,q+1)]+((long long)Tmp[p]*dp[v][q])%MOD)%MOD;
                }
                else
                {
                    dp[x][max(p,q+1)]=((long long)dp[x][max(p,q+1)]+((long long)Tmp[p]*dp[v][q])%MOD)%MOD;
                }
            }
        }
    }
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&k);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs(1,0);
    int Res=0;
    for(int i=0;i<=k;i++)
    {
        Res=((long long)Res+dp[1][i])%MOD;
    }
    printf("%d\n",Res);
}

ABC219Ex

没网了只能写题解/kk

不难看出这玩意就是左右来回走,也不难想到区间\(dp\)

不过只用这些是无法统计答案的,也不可能加上时间这一维度

考虑对问题进行一些转化,如果我们能事先知道最后要选\(k\)个点,那么我们每走一步答案都会\(-1\)

于是乎可以加上还要选多少个这一维度,然后就可以转移了

Show Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=305;
struct node{
    int x,len;
}a[MAXN];
bool operator<(node x,node y)
{
    return x.x<y.x;
}
int n;
int dp[MAXN][MAXN][MAXN][2];
signed main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld %lld",&a[i].x,&a[i].len);
    }
    a[++n]=(node){0,0};
    sort(a+1,a+1+n);
    int Key;
    for(int i=1;i<=n;i++)
    {
        if(a[i].x==0&&a[i].len==0)
        {
            Key=i;
        }    
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            for(int k=0;k<=n;k++)
            {
                dp[i][j][k][0]=-1e18;
                dp[i][j][k][1]=-1e18;
            }
        }
    }
    for(int i=0;i<=n;i++)
    {
        dp[Key][Key][i][0]=dp[Key][Key][i][1]=0;
    }
    for(int len=1;len<n;len++)
    {
        for(int l=1;l+len-1<=n;l++)
        {
            int r=l+len-1;
            for(int k=0;k<=n;k++)
            {
                if(dp[l][r][k][0]!=-0x3f3f3f3f)
                {
                    if(l>=2)
                    {
                        int Ned=(k*(a[l].x-a[l-1].x));
                        dp[l-1][r][k][0]=max(dp[l-1][r][k][0],dp[l][r][k][0]-Ned);
                        if(k)
                        {
                            dp[l-1][r][k-1][0]=max(dp[l-1][r][k-1][0],dp[l][r][k][0]-Ned+a[l-1].len);
                        }
                    }

                    if(r<n)
                    {
                        int Ned=(k*(a[r+1].x-a[l].x));
                        dp[l][r+1][k][1]=max(dp[l][r+1][k][1],dp[l][r][k][0]-Ned);
                        if(k)
                        {
                            dp[l][r+1][k-1][1]=max(dp[l][r+1][k-1][1],dp[l][r][k][0]-Ned+a[r+1].len);
                        }
                    }
                }

                if(dp[l][r][k][1]!=-0x3f3f3f3f)
                {
                    if(l>=2)
                    {
                        int Ned=(k*(a[r].x-a[l-1].x));
                        dp[l-1][r][k][0]=max(dp[l-1][r][k][0],dp[l][r][k][1]-Ned);
                        if(k)
                        {
                            dp[l-1][r][k-1][0]=max(dp[l-1][r][k-1][0],dp[l][r][k][1]-Ned+a[l-1].len);
                        }
                    }

                    if(r<n)
                    {
                        int Ned=(k*(a[r+1].x-a[r].x));
                        dp[l][r+1][k][1]=max(dp[l][r+1][k][1],dp[l][r][k][1]-Ned);
                        if(k)
                        {
                            dp[l][r+1][k-1][1]=max(dp[l][r+1][k-1][1],dp[l][r][k][1]-Ned+a[r+1].len);
                        }
                    }
                }
            }
        }
    }
    printf("%lld\n",max(dp[1][n][0][0],dp[1][n][0][1]));
}

[HNOI2019] 校园旅行

喵喵题

首先30pts的做法,我们可以直接维护每个点对是否可行,用队列拓展即可

这个做法的瓶颈在于如果我们固定某个点对,那么相当于会遍历整张图,时间复杂度\(O(nm)\)

考虑删去一些不必要的边,我们发现从两端拓展时如果一直是相同颜色的话只和奇偶性有关,仔细一想不同色同样是这样

进一步考虑,不同色的情况可以发现就是个二分图,不管怎么走奇偶性不变,所以只用保留连通图的一个生成树

而同色的同样如果是二分图就只保留生成树,否则存在奇环,无所谓奇偶可以直接连个自环

这样搞完后边数就是\(O(n)\)的了

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=5005;
int n,m,q;
int x,y;
char s[MAXN];
vector<int>g[MAXN];
vector<int>G[MAXN];
int fa[MAXN];
int find(int x)
{
	if(fa[x]==x)
	{
		return x;
	}
	fa[x]=find(fa[x]);
	return fa[x];
}
void unionn(int i,int j)
{
	fa[find(i)]=find(j);
	return;
}
int vis[MAXN];
int col[MAXN];

bool f=0;
void dfs1(int x,int c)
{
	if(col[x])
	{
		if(col[x]==c)
		{

		}
		else
		{
			f=1;
		}
		return;
	}
	col[x]=c;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(s[v]==s[x])
		{
			dfs1(v,(c==1)?2:1);
			if(find(v)!=find(x))
			{
				G[x].push_back(v);
				G[v].push_back(x);
				unionn(x,v);
			}
		}
	}
}
void dfs2(int x)
{
	if(vis[x])
	{
		return;
	}
	vis[x]=1;
	for(int i=0;i<g[x].size();i++)
	{
		int v=g[x][i];
		if(s[v]!=s[x])
		{
			dfs2(v);
			if(find(v)!=find(x))
			{
				G[x].push_back(v);
				G[v].push_back(x);
				unionn(x,v);
			}
		}
	}
}
int dp[MAXN][MAXN];
queue<pair<int,int> >Q;
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
	scanf("%d %d %d",&n,&m,&q);
	scanf("%s",s+1);
	for(int i=1;i<=m;i++)
	{
		scanf("%d %d",&x,&y);
		g[x].push_back(y);
		g[y].push_back(x);
	}
	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
	}
	for(int i=1;i<=n;i++)
	{
		if(!col[i])
		{	
			f=0;
			dfs1(i,1);
			if(f)
			{
				G[i].push_back(i);
			}			
		}
	}	

	for(int i=1;i<=n;i++)
	{
		fa[i]=i;
	}

	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
		{
			dfs2(i);
		}
	}

	for(int i=1;i<=n;i++)
	{	
		Q.push(make_pair(i,i));
		for(int j=0;j<g[i].size();j++)
		{
			int v=g[i][j];
			if(s[i]==s[v])
			{
				if(i<v)
				{
					Q.push(make_pair(i,v));
				}
			}
		}
	}
	while(Q.size())
	{
		pair<int,int>Tmp=Q.front();
		Q.pop();
		if(dp[Tmp.first][Tmp.second])
		{
			continue;
		}
		dp[Tmp.first][Tmp.second]=1;
		for(int i=0;i<G[Tmp.first].size();i++)
		{
			for(int j=0;j<G[Tmp.second].size();j++)
			{
				int u=G[Tmp.first][i];
				int v=G[Tmp.second][j];
				if(s[u]==s[v])
				{
					if(u>v)
					{
						swap(u,v);
					}
					Q.push(make_pair(u,v));
				}
			}
		}
	}
	while(q--)
	{
		scanf("%d %d",&x,&y);
		if(x>y)
		{
			swap(x,y);
		}
		if(dp[x][y])
		{
			printf("YES\n");
		}
		else
		{
			printf("NO\n");
		}
	}

}

ABC274Ex

长见识了,竟然可以用矩阵表示\(Hash\)

如果是数字串\(Hash\),两个数字串相加出来\(Hash\)同样相加

我们发现原因在于分配律

考虑什么和\(\oplus\)有分配律

注意到\((a\oplus b)\&c=(a\&c)\oplus(b\&c)\)

我们可以用\(\&\)代替乘法

不过\(\&\)的次幂没啥意义,注意到\((\oplus,\&)\)可以广义矩乘,发现同样有结合律

于是我们可以随机一个\(M\times M\)的矩阵\(P\)作为进制直接\(Hash\)找到最长相同的位置

这里复杂度有亿点高,首先矩乘得优化一维,另外这里用倍增不能用二分

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=6e5+5;
mt19937 Niuzi(114514);
int n,q;
long long a[MAXN];

int l1,l2,l3,r1,r2,r3;
struct Martix{
    int n,m;
    long long val[64];

    void clear()
    {
        memset(val,0,sizeof(val));
    }
    Martix operator*(const Martix x)const{
        Martix Res;
        Res.clear();
        Res.n=n;
        Res.m=x.m;
        for(int i=0;i<n;i++)
        {
            for(int k=0;k<m;k++)
            {
                if((val[i]>>k)&1)
                {
                    Res.val[i]^=x.val[k];
                }
            }
        }
        return Res;
    }
}P;

Martix Po[21];
long long dp[MAXN][21];
long long Mul(long long x,Martix A)
{
    long long Res=0;
    for(int k=0;k<A.m;k++)
    {
        if((x>>k)&1)
        {
            Res^=A.val[k];
        }
    }
    return Res;
}
int main()
{
    // freopen("rnm.txt","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
    }
    P.n=P.m=64;
    for(int i=0;i<63;i++)
    {
        P.val[i]=(((long long)Niuzi())%2147483648)*(((long long)Niuzi())%2147483648);
    }
    Po[0]=P;
    for(int i=1;i<=20;i++)
    {
        Po[i]=Po[i-1]*Po[i-1];
    }
    for(int i=1;i<=n;i++)
    {
        dp[i][0]=a[i];
    }
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dp[i][j]=Mul(dp[i][j-1],Po[j-1])^dp[i+(1<<(j-1))][j-1];
        }
    }
    int Tot=0;
    while(q--)
    {
        scanf("%d %d %d %d %d %d",&l1,&r1,&l2,&r2,&l3,&r3);
        ++Tot;
        int Len=min(r1-l1+1,r3-l3+1);
        int n1=l1;
        int n2=l2;
        int n3=l3;
        for(int i=20;i>=0;i--)
        {
            if(n1+(1<<i)-1<=r1&&n3+(1<<i)-1<=r3)
            {
                long long R1=dp[n1][i]^dp[n2][i];
                long long R2=dp[n3][i];
                if(R1==R2)
                {
                    n1+=(1ll<<i);
                    n2+=(1ll<<i);
                    n3+=(1ll<<i);
                }
            }
            
        }
        if((n1<=r1&&n3<=r3&&((a[n1]^a[n2])<a[n3]))||(n1>r1&&r3-l3>r1-l1))
        {
            
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
    }
}

ARC093F

长见识了

这题最好不要开头就直接状压\(m\)个人,很明显这里有不好做

我们可以发现第一个人的位置是无所谓的,假设放在\(0\)号位

考虑把完全二叉树建出来

然后你会发现对于\(0\)到根路径上的右儿子会和\(1\)比较,所以我们的目的就是不能让\(A\)中的元素成为这些右儿子子树内的最小值

设从\(0\)到根组成的集合依次\(g_i\)

这里直接对\(g\)状压,然后你发现这玩意可以容斥搞一下

我们发现这个过程可以考虑\(A_i\)是否填入一个\(g\)中,对于不填的情况不好搞,所以要容斥,同时为了保证填的单调性,我们从大到小枚举\(A_i\)

\(dp_{i,S}\)表示\([i,m]\)\(g\)\(S\)已被填,其他任意的方案数

由于限制的是前缀,我们可以得到\(A_i\)前面有多少个被填的,所以不难得到转移

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=1e5+5;
int Pow(int a,int b,int p)
{
    int res=1;
    int base=a;
    while(b)
    {
        if(b&1)
        {
            res=((long long)res*base)%p;
        }
        base=((long long)base*base)%p;
        b>>=1;
    }
    return res;
}
int inv(int a,int p)
{
    return Pow(a,p-2,p);
}
int fac[MAXN];
int inv_fac[MAXN];
int C(int n,int m)
{
    if(n<m||m<0)
    {
        return 0;
    }
    if(n==m||m==0)
    {
        return 1;
    }
    return ((((long long)fac[n]*inv_fac[m])%MOD)*inv_fac[n-m])%MOD;
}
int n,m;
int A[MAXN];
int dp[17][MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    fac[0]=1;
    for(int i=1;i<=MAXN-5;i++)
    {
        fac[i]=((long long)fac[i-1]*i)%MOD;
    }
    inv_fac[MAXN-5]=inv(fac[MAXN-5],MOD);
    for(int i=MAXN-5-1;i>=0;i--)
    {
        inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
    }
    scanf("%d %d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&A[i]);
        A[i]--;
    }
    reverse(A+1,A+1+m);
    dp[0][0]=1;
    for(int i=1;i<=m;i++)
    {
        for(int S=0;S<(1<<n);S++)
        {
            dp[i][S]=((long long)dp[i][S]+dp[i-1][S])%MOD;
            for(int k=0;k<n;k++)
            {
                if((S>>k)&1)
                {

                }
                else
                {
                    int Tyk=((long long)dp[i-1][S]*C((1<<n)-1-A[i]-S,(1<<k)-1))%MOD;
                    Tyk=((long long)Tyk*fac[(1<<k)])%MOD;
                    dp[i][S|(1<<k)]=((long long)dp[i][S|(1<<k)]+Tyk)%MOD;
                }
            }
        }
    }
    int Res=0;
    for(int i=0;i<(1<<n);i++)
    {
        int Rp=dp[m][i];
        Rp=((long long)Rp*fac[(1<<n)-1-i])%MOD;
        if(__builtin_popcount(i)&1)
        {
            Res=((long long)Res-Rp+MOD)%MOD;
        }
        else
        {
            Res=((long long)Res+Rp)%MOD;
        }
    }
    Res=((long long)Res*(1<<n))%MOD;
    printf("%d\n",Res);
}   

[ARC070E] NarrowRectangles

首先考虑\(O(n^2)dp\),设\(dp_{i,j}\)表示前\(i\)个线段其右端点在\(j\)的最小代价,令\(len_i=R_i-L_i\)

\[dp_{i,j}=|R_i-j|+Min_{k=j-len_i}^{j+len_{i-1}}dp_{i-1,k} \]

可以发现如果把\(dp\)抽象为函数,则\(Min_{k=j-len_i}^{j+len_{i-1}}dp_{i-1,k}\)显然在\(dp_{i-1}\)为凹函数的情况下同样为凹函数

\(|R_i-j|\)同样为凹函数,则\(dp_i\)同样为凹函数

再模拟一下可以发现,\(dp_i\)的斜率为整数,且中间有连续的一段斜率为\(0\),设\(L,R\)一段斜率为\(0\)

\[Min_{k=j-len_i}^{j+len_{i-1}}dp_{i-1,k}=\left\{ \begin{aligned} dp_{i-1,k+len_{i-1}},k+len_{i-1}\le L\\ dp_{i-1,L},L-len_{i-1}\le k\le R+len_i\\ dp_{i-1,k-len_i},k-len_{i}\ge R \end{aligned} \right. \]

可以发现斜率小于\(0\)左移\(len_{i-1}\),大于\(0\)右移\(len_i\),如果访问位置,可以用\(lazy\)标记

在考虑\(|R_i-j|\),反应到斜率就是\(<R_i\)\(-1\),\(>R_i\)\(+1\)(如果值域小一点其实可以用线段树)

然后考虑\(R_i\)的位置对函数的影响

\(R_i<L\),则原来斜率\(=0\)的会变为\(1\),而原来的\(-1\)便为0

如果我们维护每个\(<0\)斜率的右端点,\(>0\)斜率的左端点,那就可以每个斜率包括长度为\(0\)的都用堆维护

最后的答案我们如果知道斜率来算是可以直接求出来的

但如果维护每次斜率为\(0\)的答案,可以发现,每次取\(Min\)\(L,R\)至少有一个点斜率不变,就可以把答案的取值设为两个端点中的一个即可

JOI2016 Skyscraper/ZJOI2012波浪

两个基本上就是一道题,就是\(JOI\)的那个,\(ZJOI\)有点枸捌

对于这种问题,如果不能按位考虑,我们就可以考虑插入

至于怎么维护插入后的值,如果我们从小到大插,也不知道这个元素左右两边的信息

考虑维护连通块,这个类似于某道\(ABC\),我们可以发现当前未确定的就是联通块的两边

这里还要把边界考虑进去

如果直接算\(A_i\)的贡献,这里会出现负值,我们可以对其差分,每次插完后产生的贡献即为未确定一边的个数\(\times(A_i-A_{i-1})\)

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int MAXM=5005;
long double dp[2][MAXN][MAXM][3];
__float128 f[2][MAXN][MAXM][3];
int n,m,K;
int a[MAXN];
void out(__float128 ans){
        if(ans + 1e-14 >= 1){cout << "1." << string(K,'0') << endl; return;}
	cout << "0.";
	ans *= 10;
	for(int i = 1 ; i <= K ; ++i){
		cout << (int)(ans + (K == i) * 0.5);
		ans = (ans - (int)ans) * 10;
	}
}
signed main()
{  
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d %d",&n,&m,&K);
    for(int i=1;i<=n;i++)
    {
        a[i]=i;
    }
    sort(a+1,a+1+n);
    if(K<=20)
    {
        dp[0][0][0][0]=1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=i;j++)
            {
                for(int k=0;k<=m;k++)
                {
                    for(int d=0;d<=2;d++)
                    {
                        dp[(i&1)][j][k][d]=0;
                    }
                }
            }
            for(int j=0;j<i;j++)
            {
                for(int k=0;k<=m;k++)
                {
                    for(int d=0;d<=2;d++)
                    {
                        if(dp[(i-1)&1][j][k][d])
                        {

                            int tk=(k+(a[i]-a[i-1])*(2*j-d));
                            if(tk<=m)
                            {
                                if(d<2)
                                {
                                    dp[i&1][j+1][tk][d+1]=(dp[i&1][j+1][tk][d+1]+dp[(i-1)&1][j][k][d]*(2-d)*1.0/i);
                                    dp[i&1][j][tk][d+1]=(dp[i&1][j][tk][d+1]+dp[(i-1)&1][j][k][d]*(2-d)*1.0/i);
                                }
                                dp[i&1][j+1][tk][d]=(dp[i&1][j+1][tk][d]+dp[(i-1)&1][j][k][d]*(j+1-d)*1.0/i);
                                if(j>=1)
                                {
                                    dp[i&1][j][tk][d]=(dp[i&1][j][tk][d]+dp[(i-1)&1][j][k][d]*(2*j-d)*1.0/i);
                                }
                                if(j>=2)
                                {
                                    dp[i&1][j-1][tk][d]=(dp[i&1][j-1][tk][d]+dp[(i-1)&1][j][k][d]*(j-1)*1.0/i);
                                }
                            }
                        }
                    }
                }
            }
        }

        long double Res=0;
        for(int i=0;i<m;i++)
        {
            Res+=dp[n&1][1][i][2];
        }
        Res=1-Res;
        string dkf;
        dkf+="%.";
        dkf+=to_string(K);
        dkf+="LF";
        char sta[105];
        strcpy(sta,dkf.c_str());
        printf(sta,Res);
        return 0;
    }
    f[0][0][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=i;j++)
        {
            for(int k=0;k<=m;k++)
            {
                for(int d=0;d<=2;d++)
                {
                    f[(i&1)][j][k][d]=0;
                }
            }
        }
        for(int j=0;j<i;j++)
        {
            for(int k=0;k<=m;k++)
            {
                for(int d=0;d<=2;d++)
                {
                    if(f[(i-1)&1][j][k][d])
                    {

                        int tk=(k+(a[i]-a[i-1])*(2*j-d));
                        if(tk<=m)
                        {
                            if(d<2)
                            {
                                f[i&1][j+1][tk][d+1]=(f[i&1][j+1][tk][d+1]+f[(i-1)&1][j][k][d]*(2-d)*1.0/i);
                                f[i&1][j][tk][d+1]=(f[i&1][j][tk][d+1]+f[(i-1)&1][j][k][d]*(2-d)*1.0/i);
                            }
                            f[i&1][j+1][tk][d]=(f[i&1][j+1][tk][d]+f[(i-1)&1][j][k][d]*(j+1-d)*1.0/i);
                            if(j>=1)
                            {
                                f[i&1][j][tk][d]=(f[i&1][j][tk][d]+f[(i-1)&1][j][k][d]*(2*j-d)*1.0/i);
                            }
                            if(j>=2)
                            {
                                f[i&1][j-1][tk][d]=(f[i&1][j-1][tk][d]+f[(i-1)&1][j][k][d]*(j-1)*1.0/i);
                            }
                        }
                    }
                }
            }
        }
    }

    __float128 Res=0;
    for(int i=0;i<m;i++)
    {
        Res+=f[n&1][1][i][2];
    }
    Res=1-Res;
    out(Res);
}

ARC102F

鞲菝结论

观察\(P_{i-1}<P_i<P_{i+1}\)交换后变成\(P_{i-1}>P_i>P_{i+1}\)

逆序对减少了\(3\)

而交换是同奇偶交换

因此每次操作会对逆序对\(-3\)同时奇偶逆序对中的一个\(-1\)

\(c_0\)为逆序对数,\(c_1\)为奇数位置逆序对数,\(c_2\)为偶数

因此合法操作序列一定满足\(c_0=3(c_1+c_2)\)

充分性不大会

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+5;
int n;
int a[MAXN];
int Bit[MAXN];
int lowbit(int x)
{
    return x&(-x);
}
void update(int k,int x)
{
    for(int i=k;i<=n;i+=lowbit(i))
    {
        Bit[i]+=x;
    }
}
int Sum(int k)
{
    int res=0;
    for(int i=k;i>=1;i-=lowbit(i))
    {
        res+=Bit[i];
    }
    return res;
}
signed main()
{
	// freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);       
    }
    memset(Bit,0,sizeof(Bit));
    int c0=0;
    for(int i=n;i>=1;i--)
    {
        c0+=Sum(a[i]);
        update(a[i],1);
    }

    memset(Bit,0,sizeof(Bit));
    int c1=0;
    for(int i=n;i>=1;i-=2)
    {
        c1+=Sum(a[i]);
        update(a[i],1);
    }
    memset(Bit,0,sizeof(Bit));
    int c2=0;
    for(int i=n-1;i>=1;i-=2)
    {
        c2+=Sum(a[i]);
        update(a[i],1);
    }
   // printf("%d %d %d??\n",c0,c1,c2);
    if((c1+c2)*3==c0)
    {
        printf("Yes\n");
    }
    else
    {
        printf("No\n");
    }
}

CF1886F Diamond Theft

很有趣的题

不难发现对于\(t=1/2\),其实最多使用一次,答案也不超过\(2n+2\)

然后对于\(t=3\),我最开始以为是贪心填\(s\)大的,不过似乎很容易\(Hack\)

考虑枚举钻石\(1\)的时间\(d\),我们可以得到\(t=1\)的最早时间

不难发现答案是\(n+2\)加上\(t=3\)额外用的

不妨倒着做,钻石\(2\)时间为\(0\),这样我们就可以得到\(t=2\)时间区间\([1,s_i]\),\(t=1,[d+1,d+s_i]\)

对于\(t=3\),实际上它最多可以被拆分为两个区间,我们假定它先是\([1,s_i]\)

考虑如何判断合法,我们发现左端点只有\(1,d+1\)

这里我们就只需分开判定左端点为\(d+1\)的能否选满左端点为\(1\)的能否选满

因为\(\ge d+1\)的满足的可以往前挪

先判定第一个,不满足就拆分\(3\)区间,最后判定第二个即可

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=10005;
int n;
struct node{
    int t,s;
}a[MAXN];
int C[MAXN];
int D[MAXN];
int T[MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    if(n==0)
    {
        printf("2");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a[i].t,&a[i].s);
    }
    int Res=0x3f3f3f3f;
    for(int d=1;d<=2*n;d++)
    {
        int Tot=0;
        for(int i=1;i<=3*n;i++)
        {
            C[i]=0;
            D[i]=0;
            T[i]=0;
        }
        int Ns=0;
        for(int i=1;i<=n;i++)
        {
            if(a[i].t==1)
            {
                C[d+a[i].s]++;
                D[d+a[i].s]++;
            }
            else if(a[i].t==2)
            {
                D[a[i].s]++;
            }
            else
            {
                if(a[i].s>d)
                {
                    C[a[i].s]++;
                    D[a[i].s]++;
                    T[a[i].s]++;
                }
                else
                {
                    D[a[i].s]++;
                    D[a[i].s+d]++;
                    C[a[i].s+d]++;
                    Ns++;
                }
            }
        }       
        D[d]++;
        int S=0;
        priority_queue<int>Q;
        
        for(int r=d+1;r<=3*n;r++)
        {
            S+=C[r];
            while(T[r]--)
            {
                Q.push(r);
            }
            while(r-d<S)
            {
                if(!Q.size())
                {
                    Tot=0x3f3f3f3f;
                    break;
                }
                else
                {
                    int v=Q.top();
                    Q.pop();
                    S--;
                    Ns++;
                    D[v+d]++;
                    C[v+d]++;
                }
            }
        }
        S=0;
        for(int i=1;i<=3*n;i++)
        {
            S+=D[i];
            if(S>i)
            {
                Tot=0x3f3f3f3f;
            }
            else
            {

            }
        }
        Tot=max(Ns+n+2,Tot);
    //   printf("%d %d %d::\n",d,Tot,Ns);
        Res=min(Res,Tot);
    }
    if(Res>=0x3f3f3f3f)
    {
        printf("-1\n");
        return 0;
    }
    printf("%d\n",Res);
}

CF1874E

暴力:\(dp_{i,j}\)表示\(i\)个数\(fun\)\(j\)方案数

转移\(dp_{i,j}=\sum dp_{k,p}\times dp_{i-1-k,j-p-i}\)

第二维用\(OGF\)表示

\(F_i(x)=\sum F_k(x)F_{i-k-1}(x)x^i\)

厉害得地方来了,直接求出\(n^2\)个点值表示,然后用这些拉插一下

\(n^4\)有点卡厂

是不是很多\(FFT\)都可以这样优化个\(log\)

CF1860F

考虑两个直线相对位置最多变一次

这里我们处理出所有交点,直接枚举交点的情况即可

括号序用线段树维护

有点卡精度

为啥没做,因为没做到这

CF1861F

不可能想到的题

枚举\(j\)作为最大,二分第二大的\(k\),考虑网络流建图,建出来是个二分图,左边\(4\),右边\(n\)

我们直接枚举\(4\)个点的断边情况,得到另外\(n\)的断边式子,然后预处理一下即可

话说这是怎么想到网络流的

原来某一部点数较少可以直接枚举呀/kk

CF1879F

卡厂题

存活轮数为\(\lceil\dfrac{a_i}{x}\rceil h_i\)

考虑每个\(x\)的贡献,对于\(\lceil\dfrac{a_i}{x}\rceil\)相同的几段维护最大次大即可

关键是卡厂,这个得用\(st\)表,得注意不能重复

CF1845E

一个朴素的\(dp\)

考虑前\(i\)个位置填了\(j\)\(1\)最少用了\(k\)步的方案数,这里我们计算步数是\(1\rightarrow 0\)

直接这样做是\(n^3\)的(不过卡卡常能过

考虑以一个点作为分界点,把右边的\(1\)全部移到左边,可以发现步数\(k\)次最多移\(\sqrt k\)\(1\),因为可以发现距离为\(1\)的最多\(1\)个,距离为\(2\)的最多\(2\)个...因此步数为\(k\)最多挪\(\sqrt k\)

因此固定\(i,k\),对应的\(j\)只有\(2\sqrt k\)种取值,直接转即可

CF1849F

最开始想直接\(Tire\)树上贪心,不过有的情况要分裂到多个子树

不妨想一个暴力的思路,直接暴力连\((i,j,a_i\oplus a_j)\)的边

如果答案可以为\(x\),则权\(\le x\)的边构成的导出子图是个二分图

这里不用我们确定最小权值,我们发现图联通即可确定二分图,这里直接做最小异或生成树即可,就算答案不是树边,答案构成的环也是不可替代的

CF1874B

这个确实没思路

拆位,对于相同的\((m,a,b)\),怎么操作都相同

我们考虑记录对于\((m,a,b)\)限制为\((c,d)\)或空的最小步数,状态有\(5^8\)

CF1874D

推一下式子,不难得到\(\sum\limits_{i=1}^n\dfrac{2s_i}{a_i}-n\)

这个直接\(dp\)\(n^3\)的,不难发现\(a\)不减,枚举填的\(a\)直接调和级数即可

CF1876D

反正我想不到

第二个条件不难转换成相同值\(RG\)交替

第一个条件你发现就是总方案减相同的\(/2\)

对于相同的,我们给相邻两个值相同的看成一段区间,区间不能互相包含,有交则是一个联通块,联通块内随便填

CF1148H Holy Diver

对于区间\(mex\),我们有可以离线维护出所有\(mex\)段的方法

考虑\(f_l\)\([l,r]\)\(mex\),\(f_l\)明显单调不升

可以发现添加一个\(x\),造成的影响为\(mex=x\)的段\(l,r\)

我们考虑处理这些段,每次二分一个\(y\)使得\(y\)最后出现的位置\(pos<r\),则\((pos,r]\)\(mex\)\(y\),继续划分

可以发现是这样做每次会划分一些段出来,时间复杂度均摊是对的

然后考虑处理答案,我们将每个段的左端点作为\(x\)轴,右端点作为\(y\)轴,每次查询就找\(([l,r],[l,r])\)

我们把每个\(mex\)段拆成两个\(3-side\)的矩形类似于【

可以发现每个点的贡献类似于一个一次函数,主席树维护一下,用\(set\)存一下每个颜色的编号

空间复杂度应该是有问题的,不过似乎卡不满?,或者有哪位可以证明一下

Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=2e5+5;

int n;
int ql,qr,k,a;
long long las;


set<pair<pair<int,int>,pair<int,int> > >S;
struct Seg_node1{
    int lc,rc;
    int mni;
};
struct Seg1{
    Seg_node1 Tree[MAXN*4];
    int cnt_node;
    int rt;
    void push_up(int p)
    {
        Tree[p].mni=min(Tree[ls].mni,Tree[rs].mni);
    }
    void Build(int &p,int l,int r)
    {
        p=++cnt_node;
        if(l==r)
        {
            Tree[p].mni=0;
            return;
        }
        int mid=(l+r)>>1;
        Build(ls,l,mid);
        Build(rs,mid+1,r);
        push_up(p);
    }
    void Insert(int &p,int l,int r,int k,int x)
    {
        if(l==r)
        {
            Tree[p].mni=x;
            return;
        }
        int mid=(l+r)>>1;
        if(k<=mid)
        {
            Insert(ls,l,mid,k,x);
        }
        else
        {
            Insert(rs,mid+1,r,k,x);
        }
        push_up(p);
    }
    int Query(int p,int l,int r,int ql,int qr,int x)
    {
        if(ql>qr)
        {
            return -1;
        }
        if(!p)
        {
            return -1;
        }
        if(Tree[p].mni>=x)
        {
            return -1;
        }
        if(l==r)
        {
            return l;
        }
        int mid=(l+r)>>1;
        int Res=-1;
        if(ql<=mid)
        {
            Res=Query(ls,l,mid,ql,qr,x);
        }
        if(Res!=-1)
        {
            return Res;
        }
        if(qr>mid)
        {
            Res=Query(rs,mid+1,r,ql,qr,x);
        }
        return Res;
    }
    int Qx(int p,int l,int r,int k)
    {
        if(l==r)
        {
            return Tree[p].mni;
        }
        int mid=(l+r)>>1;
        if(k<=mid)
        {
            return Qx(ls,l,mid,k);
        }
        else
        {
            return Qx(rs,mid+1,r,k);
        }

    }
}t;

struct Seg_node{
    int lc,rc;
    long long dat1;   
    long long dat2;
    long long lazy1;
    long long lazy2;
};

int Outpx=0;
struct Seg{
    Seg_node Tree[MAXN*125];
    
    int cnt_node;
    int Update(int o,int l,int r,int ql,int qr,int x)
    {
        int p=++cnt_node;
        Tree[p]=Tree[o];
        if(cnt_node>(MAXN*125)-5)
        {
            printf("jsff\n");
            exit(0);
        }
        int op=1;
        if(x<0)
        {
            op=-1;
        }
        if(min(r,qr)>=max(l,ql))
        {
            Tree[p].dat1+=(min(r,qr)-max(l,ql)+1)*op;
            Tree[p].dat2+=(long long)x*(min(r,qr)-max(l,ql)+1);
        }
        
        if(l>=ql&&r<=qr)
        {
            Tree[p].lazy1+=op;
            Tree[p].lazy2+=x;
            return p;
        }     

        int mid=(l+r)>>1;
        if(ql<=mid)
        {
            ls=Update(ls,l,mid,ql,qr,x);
        }
        if(qr>mid)
        {
            rs=Update(rs,mid+1,r,ql,qr,x);
        }

        return p;
    }
    long long Qry(int p,int l,int r,int ql,int qr,int x)
    {
        if(!p)
        {
            return 0;
        }
        if(l>=ql&&r<=qr)
        {
            return ((long long)x+1)*(Tree[p].dat1)-Tree[p].dat2;
        }
        long long Res=0;

        if(min(r,qr)>=max(l,ql))
        {
            long long d1=(min(r,qr)-max(l,ql)+1)*Tree[p].lazy1;
            long long d2=(long long)Tree[p].lazy2*(min(r,qr)-max(l,ql)+1);
            Res+=((long long)x+1)*d1-d2;
        }
        int mid=(l+r)>>1;
        if(ql<=mid)
        {
            Res+=Qry(ls,l,mid,ql,qr,x);
        }
        if(qr>mid)
        {
            Res+=Qry(rs,mid+1,r,ql,qr,x);
        }
        return Res;
    }
}T;

map<int,int>Tix;
set<pair<pair<int,int>,int> >Rt;
int Rjx;
void Uplord(pair<pair<int,int>,pair<int,int> >Tmp,int Tim)
{
    int a=Tmp.first.first;
    // if(a==0)
    // {
    //     printf("%d %d %d??\n",Tmp.second.first,Tmp.second.second,Tim);
    // }
    
    int l=Tmp.second.first;
    int r=Tmp.second.second;
    int id=Tim;
    ++Rjx;
    if(id<0)
    {
        id=-id;
    }
    int Ix=Tix[a];
    int Dex=T.cnt_node;
   // cerr<<l<<" "<<r<<endl;
    Tix[a]=T.Update(Ix,1,n,l,r,Tim);
    if(Rt.lower_bound(make_pair(make_pair(a,id),0))!=Rt.end())
    {
        auto tsut=*Rt.lower_bound(make_pair(make_pair(a,id),0));

        if((tsut).first==make_pair(a,id))
        {
            Rt.erase(tsut);
        }
    }
    Rt.insert(make_pair(make_pair(a,id),Tix[a]));
    // if(a==0)
    // {
    //     Outpx=1;
    //     cerr<<Tim<<endl;
    //     cerr<<l<<" "<<r<<endl;
    //     cerr<<Tix[a]<<endl;
    //     cerr<<T.Qry(Tix[a],1,n,1,2,2)<<"gjjg"<<endl;
    //     Outpx=0;
    // }
    
}

int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    t.Build(t.rt,0,n+1);
    for(int id=1;id<=n;id++)
    {
        scanf("%d %d %d %d",&a,&ql,&qr,&k);
        //printf("%d %d %d %d %d??\n",a,las,id,a+las,n+1);
        a=(a+las)%(n+1);
        //printf("%d %d %d??\n",a,las,id);
        ql=(ql+las)%(id)+1;
        qr=(qr+las)%(id)+1;
        if(ql>qr)
        {
            swap(ql,qr);
        }
        k=(k+las)%(n+1);


        auto it=S.lower_bound(make_pair(make_pair(a,0),make_pair(0,0)));
        t.Insert(t.rt,0,n+1,a,id);
        
        int Tsuki=0;
        if(a==0)
        {
            Tsuki++;
        }
        //cerr<<Tsuki<<endl;
        vector<pair<pair<int,int>,pair<int,int> > >V;
        V.push_back(make_pair(make_pair(Tsuki,id),make_pair(id,id)));
        if((it!=S.end())&&((*it).first.first==a))
        {
            //cerr<<"fc"<<endl;
            int l=(*it).second.first;
            int r=(*it).second.second;
            Uplord((*it),-id);

            S.erase(it);
            
            //cerr<<a<<" "<<l<<" "<<r<<" "<<id<<endl;
            while(l<=r)
            {
                //cerr<<l<<" "<<r<<endl;
                int Ra=t.Query(t.rt,0,n+1,a+1,n,r);
                //cerr<<Ra<<endl;
                int il=max(l-1,t.Qx(1,0,n,Ra));
                if(V.back().first.first==Ra)
                {
                    V.back().second.first=il+1;
                }
                else
                {
                    V.push_back(make_pair(make_pair(Ra,id),make_pair(il+1,r)));
                }
                
                r=il;
            }
        }


        for(int i=0;i<V.size();i++)
        {
            auto pt=S.lower_bound(make_pair(make_pair(V[i].first.first,0),make_pair(0,0)));
            if((pt!=S.end())&&((*pt).first.first==V[i].first.first))
            {
                auto tmp=(*pt);
                S.erase(pt);
                //cerr<<"fc"<<endl;
               // Uplord(tmp,-id);
                S.insert(make_pair(V[i].first,make_pair(tmp.second.first,V[i].second.second)));
                Uplord(V[i],id);
                //Uplord(make_pair(V[i].first,make_pair(tmp.second.first,V[i].second.second)),id);
            }
            else
            {
                Uplord(V[i],id);
                S.insert(V[i]);
            }
        }
        
        

        


        // for(auto ft=S.begin();ft!=S.end();ft++)
        // {
        //     auto tmp=(*ft);
        //     printf("%d %d %d\n",tmp.first.first,tmp.second.first,tmp.second.second);
        // }
        //cerr<<a<<endl;
        //cerr<<S.size()<<endl;
        long long Res=0;
        auto zt=Rt.upper_bound(make_pair(make_pair(k,qr),1e9+1));
        //cerr<<Rt.size()<<endl;
         //cerr<<(*Rt.begin()).first.first<<endl;
        // for(auto ft=Rt.begin();ft!=Rt.end();ft++)
        // {
        //     auto tmp=(*ft);
        //     printf("%d %d %d\n",tmp.first.first,tmp.first.second,tmp.second);
        // }
        //printf("%d %d %d??\n",k,qr,0);
        if(zt!=Rt.begin())
        {
            //cerr<<"ffi"<<endl;
            zt--;
            auto lpx=(*zt);
            if(lpx.first.first==k)
            {
                //cerr<<"fow"<<endl;
                int rxt=lpx.second;
                Res+=T.Qry(rxt,1,n,ql,qr,qr);
            }
        }
        //cerr<<Res<<endl;
        zt=Rt.upper_bound(make_pair(make_pair(k,ql-1),1e9+1));

        if(zt!=Rt.begin())
        {
            zt--;
            auto lpx=(*zt);
            if(lpx.first.first==k)
            {
                int rxt=lpx.second;
                Res-=T.Qry(rxt,1,n,ql,qr,ql-1);
            }
        }
        las=Res;
        printf("%lld\n",Res);
    }
    //cerr<<Rjx<<endl;
}

posted @ 2023-09-17 22:14  kid_magic  阅读(66)  评论(11编辑  收藏  举报