鞍钢浐燮鑿

AGC001

[AGC001D]Arrays and Palindrome

全场最有含金量的题

不难发现奇数最多2个

然后把它们分别放在两端,让\(b_1=a_1+1\),\(b_n=a_n-1\),\(b_i=a_i,2\le i\le n-1\)

然后就行了

大概是因为这样错位了,所以\(b_i\)能保证\(i\)及以前的连在一起,且正好有一条外联边

[AGC001E] BBQ Hard

很典的转化

[AGC001F] Wide Swap

想到一个逆置换\(Q\)就简单了

相当于\(i,i+1\),能交换等价于\(|Q_i-Q_{i+1}|>k\)

为了使其最小,我发现交换相邻的逆序一定不劣

直接依次插\(Q\),用平衡树维护一下即可

Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=5e5+5;
int n,k;
int P[MAXN];
int Q[MAXN];
mt19937 Niuzi(998244353);
struct FHQ_node{
    int mx,mi;
    int lc,rc;
    int Siz;
    int ind;
    int Key;
}Tree[MAXN];
int rt,cnt_node;
int New(int id)
{
    ++cnt_node;
    Tree[cnt_node].Key=Niuzi();
    Tree[cnt_node].ind=Tree[cnt_node].mi=Tree[cnt_node].mx=id;
    Tree[cnt_node].lc=Tree[cnt_node].rc=0;
    Tree[cnt_node].Siz=1;
    return cnt_node;
}
void push_up(int p)
{
    Tree[p].mi=Tree[p].ind;
    Tree[p].mx=Tree[p].ind;
    if(ls)
    {
        Tree[p].mi=min(Tree[p].mi,Tree[ls].mi);
        Tree[p].mx=max(Tree[p].mx,Tree[ls].mx);
    }
    if(rs)
    {
        Tree[p].mi=min(Tree[p].mi,Tree[rs].mi);
        Tree[p].mx=max(Tree[p].mx,Tree[rs].mx);
    }
    Tree[p].Siz=Tree[ls].Siz+Tree[rs].Siz+1;
}
void split(int p,int k,int &x,int &y)
{
    if(!p)
    {
        x=0;
        y=0;
        return;
    }
    if(Tree[ls].Siz+1<=k)
    {
        x=p;
        split(rs,k-(Tree[ls].Siz+1),rs,y);
    }
    else
    {
        y=p;
        split(ls,k,x,ls);
    }
    push_up(p);
}
int Merge(int x,int y)
{
    if((!x)||(!y))
    {
        return x+y;
    }
    if(Tree[x].Key<Tree[y].Key)
    {
        Tree[x].rc=Merge(Tree[x].rc,y);
        push_up(x);
        return x;
    }
    else
    {
        Tree[y].lc=Merge(x,Tree[y].lc);
        push_up(y);
        return y;
    }
}
int Findp(int p,int x)
{
    if(!p)
    {
        return 0;
    }
    if(Tree[p].ind==x)
    {
        return Tree[ls].Siz+1;
    }
    else if(Tree[ls].mx>=x&&Tree[ls].mi<=x)
    {
        return Findp(ls,x);
    }
    else
    {
        return Findp(rs,x)+Tree[ls].Siz+1;
    }
}

int Find(int p,int r)
{
    if(!p)
    {
        return 0;
    }
    int res=0;
    //printf("%d %d %d %d--\n",l,r);
    if(rs)
    {
        if(Tree[rs].mi>=r)
        {
            res+=Tree[rs].Siz;
            if(Tree[p].ind>=r)
            {
                res+=1;
                res+=Find(ls,r);
                return res;
            }
            else
            {
                return res;
            }
        }
        else
        {
            return Find(rs,r);
        }
    }
    else
    {
        if(Tree[p].ind>=r)
        {
            res+=1;
            res+=Find(ls,r);
            return res;
        }
        else
        {
            return res;
        }
    }
}
int Ans[MAXN];
int id=0;
void dfs(int p)
{
    if(!p)
    {
        return;
    }
    dfs(ls);
    Ans[Tree[p].ind]=++id;
    dfs(rs);
}
void Print(int p)
{
    if(!p)
    {
        return;
    }
    Print(ls);
    printf("%d ",Tree[p].ind);
    Print(rs);
}
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",&P[i]);
        Q[P[i]]=i;
    }
    for(int i=1;i<=n;i++)
    {
        int kx=Find(rt,Q[i]+k);
        int lx,rx;
        split(rt,Tree[rt].Siz-kx,lx,rx);
        rt=Merge(lx,Merge(New(Q[i]),rx));
    }
    dfs(rt);
    for(int i=1;i<=n;i++)
    {
        printf("%d\n",Ans[i]);
    }

}

AGC002

[AGC002F] Leftmost Ball

把出现的第一个位置看做\(0\),相当于是填\(n\)\(0\),\(k-1\)\(i\),要求每个\(i\)前面有个\(0\)匹配

考虑倒着填,设\(dp_{i,j}\)表示前\(i\)种颜色后面有\(j\)\(0\)的方案数

没了??

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
const int MAXN=2005;
int n,k;
int dp[MAXN][MAXN];
int s[MAXN][MAXN];
int fac[MAXN*MAXN*2];
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 inv_fac[MAXN*MAXN*2];
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 main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    fac[0]=1;
    for(int i=1;i<=MAXN*MAXN*2-5;i++)
    {
        fac[i]=((long long)fac[i-1]*i)%MOD;
    }
    inv_fac[MAXN*MAXN*2-5]=inv(fac[MAXN*MAXN*2-5],MOD);
    for(int i=MAXN*MAXN*2-5-1;i>=1;i--)
    {
        inv_fac[i]=((long long)inv_fac[i+1]*(i+1))%MOD;
    }
    scanf("%d %d",&n,&k);

    if(k==1)
    {
        printf("1\n");
        return 0;
    }
    else
    {
        dp[0][0]=1;
        s[0][0]=1;
        for(int i=1;i<=n;i++)
        {
            s[0][i]=((long long)s[0][i-1]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<i;j++)
            {
                int Rf=(i-1)*(k-1)+j;
                int v=s[i-1][j];
                v=((long long)v*C(Rf+k-2,Rf))%MOD;
                
                dp[i][j]=((long long)dp[i][j]+v)%MOD;
                
            }

            for(int j=0;j<=n;j++)
            {
                if(j)
                {
                    s[i][j]=((long long)s[i][j-1]+dp[i][j])%MOD;
                }
                else
                {
                    s[i][j]=dp[i][j];
                }
                
            }
        }
        //printf("%d %d??\n",dp[2][0],dp[2][1]);
        int Res=s[n][n-1];
        for(int i=1;i<=n;i++)
        {
            Res=((long long)Res*i)%MOD;
        }
        printf("%d\n",Res);
    }
}

[AGC002E] Candy Piles

题读错了/kk

考虑用二元组\((i,j)\)表示一个局面

\((i,j)=(i+1,j+1)\),感性理解一下

然后你就只用找边界即可

Show Code

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n;
int a[MAXN];
int cmp(int x,int y)
{
    return x>y;
}
int 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]);
    }
    sort(a+1,a+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        if(i+1>a[i+1])
        {
            int Rest=a[i]-(i-1);
            int Pi=i;
            while(Pi<=n&&a[Pi]>=i)
            {
                ++Pi;
            }
            int Rp=Pi-i;
            if((Rest%2==0)||(Rp%2==0))
            {
                printf("First\n");
            }
            else
            {
                printf("Second\n");
            }
            break;    
        }
    }
}

AGC003

[AGC003F] Fraction of Fractal

本来想着欧拉定理

结果这里直接分个类,面数只有\(1\)

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=1e9+7;
struct Martix{
    int val[2][2];
    void clear()
    {
        memset(val,0,sizeof(val));
    }
    void init()
    {
        clear();
        for(int i=0;i<2;i++)
        {
            val[i][i]=1;
        }
    }
    Martix operator*(const Martix x)const{
        Martix Res;
        Res.clear();
        for(int k=0;k<2;k++)
        {
            for(int i=0;i<2;i++)
            {
                for(int j=0;j<2;j++)
                {
                    Res.val[i][j]=((long long)Res.val[i][j]+((long long)val[i][k]*x.val[k][j])%MOD)%MOD;
                }
            }
        }
        return Res;
    }
}A,B;
Martix Pow(Martix Base,long long b)
{
    Martix Res;
    Res.init();
    while(b)
    {
        //cerr<<b<<endl;
        if(b&1)
        {
            Res=Res*Base;
        }
        Base=Base*Base;
        
        b>>=1;
    }
    return Res;
}
int Pow(int a,long long 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 n,m;
long long k;
char s[2005][2005];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d %lld",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s[i]+1);
    }

    int tot=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(s[i][j]=='#')
            {
                ++tot;
            }
        }
    }

    int Cp=0;
    int Ca=0,Cb=0;
    int Da=0;
    int Db=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(s[i][j]!='#')
            {
                continue;
            }
            if((i+1<=n)&&(s[i+1][j]=='#'))
            {
                Da++;
            }
            else if(s[1][j]=='#'&&i==n)
            {
                Ca++;
                
            }

            if((j+1<=m)&&(s[i][j+1]=='#'))
            {
                Db++;
            }
            else if(s[i][1]=='#'&&j==m)
            {
                ++Cb;
            }
        }
    }

    
    Cp=tot;
    if(Ca&&Da&&Cb&&Db)
    {
        printf("1\n");
    }
    else if((!(Ca&&Da))&&(!(Cb&&Db)))
    {
        printf("%d\n",Pow(Cp,k-1,MOD));
    }
    else
    {
        if(k==1)
        {
            printf("1");
            return 0;
        }
        int Res=Pow(Cp,k-1,MOD);
        if((Ca&&Da))
        {
            A.clear();
            A.val[0][0]=Da;A.val[0][1]=Ca;
            B.clear();
            B.val[0][0]=Cp;B.val[0][1]=0;
            B.val[1][0]=Da;B.val[1][1]=Ca;
            A=A*Pow(B,k-2);
            Res=((long long)Res-A.val[0][0]+MOD)%MOD;
        }
        else
        {
            A.clear();
            A.val[0][0]=Db;A.val[0][1]=Cb;
            B.clear();
            B.val[0][0]=Cp;B.val[0][1]=0;
            B.val[1][0]=Db;B.val[1][1]=Cb;
            A=A*Pow(B,k-2);
            //printf("%d??\n",Res);
            Res=((long long)Res-A.val[0][0]+MOD)%MOD;
        }
        printf("%d\n",Res);
    }
}

[AGC003E] Sequential operations on Sequence

大脑萎缩/kk

先砍没用的

直接倒着做,计算出每次操作的系数即可

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n,m;
long long Q[MAXN];
int st[MAXN];
int head;
long long R[MAXN];
long long Cf[MAXN];
void solve(int x,long long B,long long Radio)
{
    if(x==1)
    {
        //printf("%d %d???\n",B,Radio);
        Cf[1]+=Radio;
        Cf[B+1]-=Radio;
        return;
    }
    long long Times=(B/Q[st[x-1]]);
    R[x-1]+=(Times*Radio);
    //printf("%d %d %d %d??\n",R[x-1],x-1,B,Q[st[x-1]]);
    long long Rt=(B%Q[st[x-1]]);
    int l=1;
    int r=x-1;
    int Key;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(Q[st[mid]]>Rt)
        {
            r=mid-1;
            Key=mid;
        }
        else
        {
            l=mid+1;
        }
    }
    solve(Key,Rt,Radio);
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&m);
    Q[0]=n;
    st[++head]=0;
    for(int i=1;i<=m;i++)
    {
        scanf("%lld",&Q[i]);
        while(head&&Q[st[head]]>=Q[i])
        {
            head--;
        }
        st[++head]=i;
    }
    R[head]=1;
    for(int i=head;i>=1;i--)
    {
       // printf("%d??\n",Q[st[i]]);
        solve(i,Q[st[i]],R[i]);
    }

    for(int i=1;i<=n;i++)
    {
        Cf[i]=Cf[i-1]+Cf[i];
        printf("%lld\n",Cf[i]);
    }
}

[AGC004E] Salvage Robots

贪心得想,我们肯定是先往一个方向走到底再回去,这里回去的时候要考虑\(x,y\)的先后顺序得跑个\(dp\)

不过讨论的情况有点多

实际上我们直接记录\(4\)个状态即可,同时可以得到当前能前往的矩形区域直接转移就好了

这确实很暴力,如果想优化空间还得是上面的做法

Show Code
#include<bits/stdc++.h>
#define x1 kfks
#define y1 nyhweisuo
#define x2 xunjie 
#define y2 nyhtebieweisuo
using namespace std;
const int MAXN=105;
int n,m;

char mp[MAXN][MAXN];

short sum[MAXN][MAXN];
short dp[MAXN][MAXN][MAXN][MAXN];
short Get(int x1,int y1,int x2,int y2)
{
    if(x1>x2)
    {
        return 0;
    }

    if(y1>y2)
    {
        return 0;
    }
    
    return sum[x2][y2]-sum[x1-1][y2]-sum[x2][y1-1]+sum[x1-1][y1-1];
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",mp[i]+1);
        for(int j=1;j<=m;j++)
        {
            sum[i][j]=sum[i-1][j]+sum[i][j-1]+(mp[i][j]=='o')-sum[i-1][j-1];
        }
    }
    int sx,sy;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(mp[i][j]=='E')
            {
                sx=i;
                sy=j;
            }
        }
    }

    short Res=0;

    for(int upx=0;upx<=n;upx++)
    {
        for(int dox=0;dox<=n;dox++)
        {   
            for(int upy=0;upy<=m;upy++)
            {
                for(int doy=0;doy<=m;doy++)
                {
                    Res=max(Res,dp[upx][dox][upy][doy]);
                    int Lx=upx+1;
                    int Rx=n-dox;

                    int Ly=upy+1;
                    int Ry=m-doy;


                    int lx=sx-dox;
                    int rx=sx+upx;

                    int ly=sy-doy;
                    int ry=sy+upy;

                    lx=max(lx,Lx);
                    ly=max(ly,Ly);

                    rx=min(rx,Rx);
                    ry=min(ry,Ry);

                    if(rx<Rx)
                    {
                        dp[upx+1][dox][upy][doy]=max((int)dp[upx+1][dox][upy][doy],dp[upx][dox][upy][doy]+Get(rx+1,ly,rx+1,ry));
                    }

                    if(lx>Lx)
                    {
                        dp[upx][dox+1][upy][doy]=max((int)dp[upx][dox+1][upy][doy],dp[upx][dox][upy][doy]+Get(lx-1,ly,lx-1,ry));

                    }


                    if(ry<Ry)
                    {
                        dp[upx][dox][upy+1][doy]=max((int)dp[upx][dox][upy+1][doy],dp[upx][dox][upy][doy]+Get(lx,ry+1,rx,ry+1));
                    }

                    if(ly>Ly)
                    {
                        dp[upx][dox][upy][doy+1]=max((int)dp[upx][dox][upy][doy+1],dp[upx][dox][upy][doy]+Get(lx,ly-1,rx,ly-1));

                    }
                }
            }
        }
    }

    printf("%d\n",(int)Res);
}

AGC005

[AGC005F] Many Easy Problems

想复杂了/kk

考虑每个点的贡献,反着考虑,发现必须要求选的点要么全在子树内要么在子树外

直接列式子,\(\sum\limits_j{\binom{n}{i}-\sum\limits_{v}\binom{Siz_v}{i}-\binom{Siz_j}{i}}\)

这玩意拆一下就是个卷积

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
const int MOD=924844033;
int Rev[MAXN*4];
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 fac[MAXN];
int inv(int a,int p)
{
    return Pow(a,p-2,p);
}
const int g=5;  
struct Poly{
    vector<int>V;
    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(V.size()<Len)
		{
			V.push_back(0);
		}
		for(int i=0;i<Len;i++)
		{
			if(i<Rev[i])
			{
				swap(V[i],V[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=V[j];
					int Yc=((long long)V[j+l]*W)%MOD;
					V[j]=((long long)Xc+Yc)%MOD;
					V[j+l]=((long long)Xc-Yc+MOD)%MOD;
				}
			}
		}
		if(type==-1)
		{
			int Liv=inv(Len,MOD); 
			for(int i=0;i<Len;i++)
			{
				V[i]=((long long)V[i]*Liv)%MOD;	
			}
		}
	}
};
Poly operator*(Poly A,Poly B){
	int N=A.V.size();
	int M=B.V.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.V[i]=((long long)A.V[i]*B.V[i])%MOD;
	 }
	 A.NTT(Lm,-1);
	 while(A.V.size()>(N+M-1))
	 {
	 	A.V.pop_back();
	 }
	 return A;
};
int n;
int x,y;
vector<int>G[MAXN];
int Siz[MAXN];
void dfs(int x,int f)
{
    Siz[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==f)
        {
            continue;
        }
        dfs(v,x);
        Siz[x]+=Siz[v];
    }
}
int Num[MAXN];

int Ca[MAXN];
int Cb[MAXN];
int Cc[MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    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);
    for(int i=1;i<=n;i++)
    {
        if(i==1)
        {
            continue;
        }
        Num[Siz[i]]++;
    }

    fac[0]=1;
    for(int i=1;i<=n;i++)
    {
        fac[i]=((long long)fac[i-1]*i)%MOD;
    }
    Poly A,B;
    A.V.resize(n+1);
    B.V.resize(n+1);

    for(int i=0;i<=n;i++)
    {
        A.V[i]=((long long)Num[i]*fac[i])%MOD;
    }
    for(int i=0;i<=n;i++)
    {
        B.V[n-i]=((long long)inv(fac[i],MOD))%MOD;
    }

    A=(A*B);
    for(int i=1;i<=n;i++)
    {
        int Res=A.V[n+i];
        Res=((long long)Res*inv(fac[i],MOD))%MOD;
        Cb[i]=Res;
        Ca[i]=((long long)n*fac[n])%MOD;
        Ca[i]=((long long)Ca[i]*inv(fac[i],MOD))%MOD;
        Ca[i]=((long long)Ca[i]*inv(fac[n-i],MOD))%MOD;
    }   

    for(int i=0;i<=n;i++)
    {
        Num[i]=0;
    }


    for(int i=1;i<=n;i++)
    {
        Num[n-Siz[i]]++;
    }
    A.V.resize(n+1,0);
    B.V.resize(n+1,0);

    for(int i=0;i<=n;i++)
    {
        A.V[i]=((long long)Num[i]*fac[i])%MOD;
    }
    for(int i=0;i<=n;i++)
    {
        B.V[n-i]=((long long)inv(fac[i],MOD))%MOD;
    }

    A=(A*B);
    for(int i=1;i<=n;i++)
    {
        int Res=A.V[n+i];
        Res=((long long)Res*inv(fac[i],MOD))%MOD;
        Cc[i]=Res;
        ///printf("%d %d %d %d\n",i,Ca[i],Cb[i],Cc[i]);
        printf("%d\n",((long long)Ca[i]-Cb[i]-Cc[i]+2ll*MOD)%MOD);
    }   
}

[AGC005E] Sugigma: The Showdown

先考虑什么时候\(-1\)

然后你发现就是找一条边\(u,v\)使得能到达且两点间距离\(>3\)

能到达就看距离是否\(A<B\)然后\(bfs\)一下

然后再考虑不是\(-1\)

由于没有上面一种情况,因此就没有类似欺骗的操作,直接选能到达里离\(B\)最远的

证明不会

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n,rx,ry;
vector<int>G[MAXN];
vector<int>g[MAXN];
int x,y;
int depa[MAXN];
int depb[MAXN];
void dfs1(int x,int f)
{
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==f)
        {
            continue;
        }
        depa[v]=depa[x]+1;
        dfs1(v,x);
    }
}
int cnt_dfn;
int Rlf[MAXN];
int dfn[MAXN];
int dp[MAXN][21];
int Lg[MAXN];
int Fa[MAXN];
void dfs2(int x,int f)
{
    dfn[x]=++cnt_dfn;
    Rlf[cnt_dfn]=x;
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        Fa[v]=x;
        depb[v]=depb[x]+1;
        dfs2(v,x);
    }
}
int V[MAXN];
int E[MAXN];
void dfs3(int x,int f)
{
    if(!V[x])
    {
        return;
    }
    E[x]=1;
    for(int i=0;i<G[x].size();i++)
    {
        int v=G[x][i];
        if(v==f)
        {
            continue;
        }
        dfs3(v,x);
    }
}
int LCA(int a,int b)
{
    if(a==b)
    {
        return a;
    }
    int l=dfn[a];
    int r=dfn[b];
    if(l>r)
    {
        swap(l,r);
    }
    l++;
    int k=Lg[r-l+1];
    int tox=min(dp[l][k],dp[r-(1<<k)+1][k]);
    return Rlf[tox];
}
int dist(int a,int b)
{
    return depb[a]+depb[b]-2*depb[LCA(a,b)];
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d %d",&n,&rx,&ry);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&x,&y);
        G[x].push_back(y);
        G[y].push_back(x);
    }
    dfs1(rx,0);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    dfs2(ry,0);
    
    for(int i=1;i<=n;i++)
    {
        if(depb[i]>depa[i])
        {
            V[i]=1;
        }
    }

    for(int i=1;i<=n;i++)
    {
        Lg[i]=log2(i);
        dp[i][0]=dfn[Fa[Rlf[i]]];
    }
    for(int j=1;(1<<j)<=n;j++)
    {
        for(int i=1;i+(1<<j)-1<=n;i++)
        {
            dp[i][j]=min(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
        }
    }
    dfs3(rx,0);
    bool f=0;
    for(int i=1;i<=n;i++)
    {
        if(E[i])
        {
            for(int j=0;j<G[i].size();j++)
            {
                int v=G[i][j];
                if(dist(i,v)>=3)
                {
                    //printf("%d %d??\n",i,v);
                    f=1;
                }
            }
        }
    }

    if(f)
    {
        printf("-1");
    }
    else
    {
        int Res=0;

        for(int i=1;i<=n;i++)
        {
            if(E[i])
            {
                Res=max(Res,2*depb[i]);

            }
        }
        printf("%d\n",Res);
    }
}

[AGC005D] ~K Perm Counting

容斥

然后直接\(dp\),每\(2k\)分一组然后直接暴力卷积即可

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MOD=924844033;
const int MAXN=2005;
int n,k;
int dp[MAXN];
int tmp[MAXN];
int f[MAXN][MAXN][2];
int fac[MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d",&n,&k);
    fac[0]=1;
    for(int i=1;i<=n;i++)
    {
        fac[i]=((long long)fac[i-1]*i)%MOD;
    }
    dp[0]=1;
    for(int l=1;l<=min(n,2*k);l++)
    {
        int Cp=0;
        for(int i=l;i<=n;i+=2*k)
        {
            Cp++;
        }
        for(int i=0;i<=Cp;i++)
        {
            for(int j=0;j<=Cp;j++)
            {
                f[i][j][0]=f[i][j][1]=0;
            }
        }
        f[0][0][0]=1;
        for(int i=1;i<=Cp;i++)
        {
            int id=l+(i-1)*2*k;
            for(int j=0;j<=Cp;j++)
            {
                if(j)
                {
                    if(id+k<=n)
                    {
                        f[i][j][1]=((long long)f[i][j][1]+f[i-1][j-1][0])%MOD;
                        f[i][j][1]=((long long)f[i][j][1]+f[i-1][j-1][1])%MOD;
                    } 

                    if(id-k>=1)
                    {
                        f[i][j][0]=((long long)f[i][j][0]+f[i-1][j-1][0])%MOD;
                    }
                }

                f[i][j][0]=((long long)f[i][j][0]+f[i-1][j][0])%MOD;
                f[i][j][0]=((long long)f[i][j][0]+f[i-1][j][1])%MOD;
            }
        }
    

        for(int i=0;i<=n;i++)
        {
            tmp[i]=dp[i];
            dp[i]=0;
        }

        for(int i=0;i<=n;i++)
        {
            for(int j=0;j<=min(i,Cp);j++)
            {
                int vx=((long long)f[Cp][j][0]+f[Cp][j][1])%MOD;
                dp[i]=((long long)dp[i]+((long long)vx*tmp[i-j])%MOD)%MOD;
            }           
        }
    }

    int Res=0;
    for(int i=0;i<=n;i++)
    {
        int v=dp[i];
        v=((long long)v*fac[n-i])%MOD;
        if(i&1)
        {
            Res=((long long)Res-v+MOD)%MOD;
        }
        else
        {
            Res=((long long)Res+v)%MOD;
        }
    }
    printf("%d\n",Res);
}

AGC006

[AGC006E] Rotate 3x3

不难将其刻画成\(n\)个有正负的数每次可以选择\(a_i,a_{i+2}\)交换并将\(i,i+1,i+2\)\(-\),使其排序成\(1-n\)

首先奇偶分类,判断其不管正负是否能排

然后你可以通过一些操作使得对\(i,i+2\)\(-\)(不会构造

然后你可以发现奇偶负号只与其奇偶性有关

同时你可以发现奇数中的操作次数会对偶数的负号个数有影响,所以只用判逆序对奇偶即可

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n;
int a[MAXN][3];
pair<int,int>v[MAXN];
int cf[MAXN];

int Pos[MAXN];
int Px[MAXN];
int Bit[MAXN];
int lowbit(int x)
{
    return x&(-x);
}
void update(int k,int x)
{
    for(int i=k;i>=1;i-=lowbit(i))
    {
        Bit[i]+=x;
    }
    return;
}
int Sum(int k)
{
    int res=0;
    for(int i=k;i<=n;i+=lowbit(i))
    {
        res+=Bit[i];
    }
    return res;
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=3;i++)
    {
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&a[j][i]);
        }
    }

    for(int i=1;i<=n;i++)
    {
        if(a[i][1]<a[i][2]&&a[i][2]<a[i][3])
        {
            if((a[i][1]-1)%3==0)
            {
                v[i]=make_pair((a[i][1]-1)/3+1,0);
            }
            else
            {
                printf("No");
                return 0;
            }
        }
        else if(a[i][1]>a[i][2]&&a[i][2]>a[i][3])
        {
            if((a[i][3]-1)%3==0)
            {
                v[i]=make_pair((a[i][3]-1)/3+1,1);
            }
            else
            {
                printf("No");
                return 0;
            }
        }
        else
        {
            printf("No");
            return 0;
        }
    }
    for(int i=1;i<=n;i++)
    {
        if((i&1)!=(v[i].first&1))
        {
            printf("No");
            return 0;
        }
        Pos[v[i].first]=i;
    }
    long long Ro=0;
    long long Re=0;
    for(int i=1;i<=n;i+=2)
    {
        Px[i]=Sum(v[i].first);
        Ro+=Px[i];
        update(v[i].first,1);
        if(v[i].second)
        {
            Re--;
        }
    }

    memset(Bit,0,sizeof(Bit));
    for(int i=2;i<=n;i+=2)
    {
        Px[i]=Sum(v[i].first);
        Re+=Px[i];
        update(v[i].first,1);
        if(v[i].second)
        {
            Ro--;
        }
    }
    if(Ro<0)
    {
        Ro=-Ro;
    }
    if(Re<0)
    {
        Re=-Re;
    }
    if(Ro%2==1)
    {
        printf("No\n");
        return 0;
    }
    if(Re%2==1)
    {
        printf("No\n");
        return 0;
    }

    printf("Yes\n");
}

[AGC006C] Rabbit Exercise

对于操作\(-a_i+a_{i-1}+a_{i+1}\),即差分\(d_i,d_i+1\)交换

然后直接置换环搞一下即可

Show Code
// LUOGU_RID: 139873219
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,m;
long long k;
int a[MAXN];
int d[MAXN];
int p[MAXN];
int vis[MAXN];
int Lorp[MAXN];
int Cnt;
void dfs(int x)
{
    if(vis[x])
    {
        return;
    }
    vis[x]=1;
    Lorp[Cnt++]=x;
    dfs(p[x]);
}
int v[MAXN];
signed main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        d[i]=a[i]-a[i-1];
    }
    scanf("%lld %lld",&m,&k);
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
    }
    for(int i=1;i<=m;i++)
    {
        int x;
        scanf("%lld",&x);
        swap(p[x],p[x+1]);
    }

    for(int i=1;i<=n;i++)
    {
        if(!vis[i])
        {
            Cnt=0;
            dfs(i);
            for(int i=0;i<Cnt;i++)
            {
                v[Lorp[i]]=d[Lorp[(i+k)%Cnt]];
            }
        }
    }
    long long now=0;
    for(int i=1;i<=n;i++)
    {
        now+=v[i];
        printf("%lld\n",now);
    }
}

AGC007

[AGC007C] Pushing Balls

操作一次中间的线段会使\(i+2,i+3,i\)替代\(i\)

而我们操作\(i\)之前的会使\(i\)变为\(i+2\)

直接列出\(d'_i\)的期望

\(d'_i=d_i+\dfrac{i(d_{i+2}-d_i)+(d_{i+3}+d_{i+2})}{2n}\)

\(d'_i=d_1+(i)x+\dfrac{i(2x)+d_1+(i+3)x+d_1+(i+2)x}{2n}\)

\(=d_1+ix+\dfrac{4ix+2d_1+5x}{2n}\)

发现\(d'\)依旧是等差数列

Show Code
#include<bits/stdc++.h>
using namespace std;
int n;
double d,x;
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %lf %lf",&n,&d,&x);
    double Res=0;
    while(n)
    {
        Res+=d+(2*n-1)*x*1.0/2;
        double tpd=d;
        double tpx=x;
        d=tpd+(2*tpd+5*tpx)*1.0/(2*n);
        x=x+2*tpx*1.0/n;
        //x+=1;
        n--;
    }
    printf("%.10lf\n",Res);
}

[AGC007D] Shik and Game

\(dp_i\)为走完前\(i\)个点最小时间

转移线段树优化一下即可

不过好像可以单调队列

Show Code
#include<bits/stdc++.h>
#define ls Tree[p].lc
#define rs Tree[p].rc
using namespace std;
const int MAXN=5e5+5;
int n,E,T;
int x[MAXN];
long long dp[MAXN];
struct Seg_node{
    int lc,rc;
    long long date;
};
struct Seg{
    Seg_node Tree[MAXN*10];
    int rt;
    int cnt_node;
    void Insert(int &p,int l,int r,int k,long long x)
    {
        if(!p)
        {
            p=++cnt_node;
            Tree[p].lc=Tree[p].rc=0;
            Tree[p].date=2e18;
        }
        Tree[p].date=min(Tree[p].date,x);
        if(l==r)
        {
            return;
        }
        int mid=((long long)l+r)>>1;
        if(k<=mid)
        {
            Insert(ls,l,mid,k,x);
        }
        else
        {
            Insert(rs,mid+1,r,k,x);
        }
    }
    long long Query(int p,int l,int r,int ql,int qr)
    {
        if(ql>qr)
        {
            return 2e18;
        }
        if(!p)
        {
            return 2e18;
        }
        if(l>=ql&&r<=qr)
        {
            return Tree[p].date;
        }
        long long Res=2e18;
        int mid=((long long)l+r)>>1;
        if(ql<=mid)
        {
            Res=min(Res,Query(ls,l,mid,ql,qr));
        }
        if(qr>mid)
        {
            Res=min(Res,Query(rs,mid+1,r,ql,qr));
        }
        return Res;
    }
}t1,t2;

int s[MAXN];
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d %d %d",&n,&E,&T);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x[i]);
    }
    x[0]=0;
    for(int i=0;i<n;i++)
    {
        s[i]=s[i-1]+(x[i+1]-x[i]);
    }
    dp[0]=0;
    t1.Insert(t1.rt,0,2e9,2*s[0],(x[1]-x[0])-s[0]-2*s[0]);
    t2.Insert(t2.rt,0,2e9,2*s[0],(x[1]-x[0])-s[0]);
    for(int i=1;i<=n;i++)
    {
        int Lit=2*s[i-1]-T;
        dp[i]=t1.Query(t1.rt,0,2e9,0,Lit)+3ll*s[i-1];
        dp[i]=min(dp[i],t2.Query(t2.rt,0,2e9,max(Lit+1,0),2e9)+s[i-1]+T);
       // printf("%lld\n",t1.Query(t1.rt,0,2e9,0,Lit));
        t1.Insert(t1.rt,0,2e9,2*s[i],dp[i]+(x[i+1]-x[i])-s[i]-2*s[i]);
        t2.Insert(t2.rt,0,2e9,2*s[i],dp[i]+(x[i+1]-x[i])-s[i]);
        //printf("%d %lld %d?\n",i,dp[i],Lit);
    }
    printf("%lld\n",E-x[n]+dp[n]);
}

[AGC007E] Shik and Travel

一直在想怎么优化贪心/kk

实际上这里我们可以用\((a,b)\)来记录所有转移点

注意\((c,d)\),如果\(a<c,b<d\),则\((c,d)\)无用

这样按\(a\)排序,\(b\)则单减

考虑用\((a,b),(c,d)\)拼接为\((a,d)\),可以发现固定\(a\),\(d\)一定

这样一次转移我们的转移点数量不会超过\(2min(Siz_x,Siz_y)\)

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n;
vector<pair<int,int> >g[MAXN];
int x,w;
vector<pair<long long,long long> >V[MAXN];
long long Lit;

void dfs(int x)
{   
    if(!g[x].size())
    {
        V[x].clear();
        V[x].push_back(make_pair(0,0));
        return;
    }
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i].first;
        dfs(v);
    }

    int ls=g[x][0].first;
    int rs=g[x][1].first;
    int wl=g[x][0].second;
    int wr=g[x][1].second;
    V[x].clear();
    int Pi=0;
    vector<pair<long long,long long> >L,R;
    for(int i=0;i<V[ls].size();i++)
    {
        auto tmp=V[ls][i];
        while(Pi<V[rs].size()&&V[rs][Pi].first+wr+tmp.second+wl<=Lit)
        {
            Pi++;
        } 
        if(Pi)
        {
            L.push_back(make_pair(V[ls][i].first+wl,V[rs][Pi-1].second+wr));
        }
    }  
    
    Pi=0;
    for(int i=0;i<V[rs].size();i++)
    {
        auto tmp=V[rs][i];
        while(Pi<V[ls].size()&&V[ls][Pi].first+wr+tmp.second+wl<=Lit)
        {
            Pi++;
        } 
        if(Pi)
        {
            R.push_back(make_pair(V[rs][i].first+wr,V[ls][Pi-1].second+wl));
        }
    }  
    V[x].clear();
    int Pl=0;
    int Pr=0;
    vector<pair<long long,long long> >Tmp;
    while(Pl<L.size()&&Pr<R.size())
    {   
        if(L[Pl]<R[Pl])
        {
            Tmp.push_back(L[Pl]);
            Pl++;        
        }
        else
        {
            Tmp.push_back(R[Pr]);
            Pr++;
        }
    }

    while(Pl<L.size())
    {
        Tmp.push_back(L[Pl]);
        Pl++;        
    }
    while(Pr<R.size())
    {
        Tmp.push_back(R[Pr]);
        Pr++;
    }
    for(int i=0;i<Tmp.size();i++)
    {
        if(!V[x].size())
        {
            V[x].push_back(Tmp[i]);
        }
        else if(Tmp[i].second<V[x].back().second)
        {
            V[x].push_back(Tmp[i]);
        }
    }
    //cerr<<x<<" "<<V[x].size()<<endl;
}   
bool check(long long mid)
{
    Lit=mid;
    dfs(1);
    return V[1].size()>=1;
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        scanf("%d %d",&x,&w);
        g[x].push_back(make_pair(i,w));
    }
    //printf("%d?\n",check(4));
    //printf("%d?\n",check(6));
    long long l=0;
    long long r=4e10;
    long long Key=-1;
    while(l<=r)
    {
        long long mid=(l+r)>>1;
        //printf("%lld %d?\n",mid,check(mid));
        if(check(mid))
        {
            Key=mid;
            r=mid-1;
        }
        else
        {
            l=mid+1;
        }
    }
    printf("%lld\n",Key);
}

[AGC007F] Shik and Copying String

大抵是考虑用队列维护出从大到小的每个拐点的信息

这个感觉多WA一下就可以了

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e6+5;
int n;
char s[MAXN];
char t[MAXN];
vector<int>Rec[27];
int Rs[MAXN];
int st[MAXN];
int head,tail;
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    scanf("%s",s+1);
    scanf("%s",t+1);
    while(n>=1&&s[n]==t[n])
    {
        n--;
    }
    if(n==0)
    {
        printf("0\n");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        Rec[s[i]-'a'].push_back(i);
    }
    if(!Rec[t[n]-'a'].size())
    {
        printf("-1");
        return 0;
    }
    int R=Rec[t[n]-'a'].back();
    Rs[n]=R;
    for(int i=n-1;i>=1;i--)
    {   
        int tox=upper_bound(Rec[t[i]-'a'].begin(),Rec[t[i]-'a'].end(),min(i,R))-Rec[t[i]-'a'].begin()-1;

        if(tox>=0)
        {
            tox=Rec[t[i]-'a'][tox];
            Rs[i]=tox;
            R=Rs[i];
        }
        else
        {
            printf("-1\n");
            return 0;
        }
    }
    
    int Res=0;
    head=1;
    tail=0;
    for(int i=n;i>=1;i--)
    {
        if(t[i]==t[i-1])
        {
            continue;
        }
        int Rx=Rs[i];
        while(head<=tail&&st[head]-(tail-head+1)>=i)//偏移量为时间戳
        {
            head++;
        }
        if(Rx!=i)
        {
            st[++tail]=Rx;
            Res=max(Res,tail-head+1);
        }
        
    }
    printf("%d\n",Res+1);
}

AGC008

[AGC008F] Black Radius

一个很有AT风格的题

如果全有,不难想到对于每个\(u\)计数\(d\)最小的

会不会出现\(f(u_1,d)=f(u_2,d)\)的情况?

把全局刨掉就不会

不难看出合法的\(d\)是一段区间

考虑\((u,d)\)不合法,也即存在\(f(u,d)=f(v,d-1)\),\(v\in son_u\),或者\(d\ge Max_u\)

不难发现对于\(u\),如果找到一个点\(x\)使得\(d(u,x)>d-2\)\(f(v,d-1)\not=f(u,d)\)

这里取\(d(u,v)=Max_u\),则\(d(u,x)=Sec_u\)

那如果\(d\ge d(u,x)+1\)就一定不合法吗\(?\),似乎是的...

对于不全有,我们考虑对\(u,s_u=0\),找到\(v,s_v=1\)与之对应,使得\(f(u,d)=f(v,d+dis(u,v))\)

这里不加证明的给出结论,\(v\)是以\(u\)为根时最大深度最小子树内的点

意会一下把

Show Code
#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+5;
int n;
int x,y;
vector<int>g[MAXN];
char s[MAXN];
int F1[MAXN];
int F2[MAXN];
int G2[MAXN];
int H1[MAXN];
int H2[MAXN];
int Fa[MAXN];
int Siz[MAXN];
void dfs1(int x,int f)
{
    H1[x]=0x3f3f3f3f;
    Siz[x]=(s[x]=='1');
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        Fa[v]=x;
        dfs1(v,x);
        F1[x]=max(F1[x],F1[v]+1);
        Siz[x]+=Siz[v];
        if(Siz[v])
        {
            H1[x]=min(H1[x],F1[v]+1);
        }
    }
}

void dfs2(int x,int f)
{
    int Maxi=F2[x];
    int Seci=0;
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        int Rw=F1[v]+1;
        if(Rw>=Maxi)
        {
            Seci=Maxi;
            Maxi=Rw;
        }
        else if(Rw>=Seci)
        {
            Seci=Rw;
        }
    }

    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        int Shox;
        int Rw=F1[v]+1;
        if(Rw==Maxi)
        {
            Shox=Seci;
        }
        else
        {
            Shox=Maxi;
        }
        F2[v]=Shox+1;
    }


    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        H2[v]=0x3f3f3f3f;
        if(Siz[1]-Siz[v])
        {
            H2[v]=F2[v];
        }
    }   
    for(int i=0;i<g[x].size();i++)
    {
        int v=g[x][i];
        if(v==f)
        {
            continue;
        }
        dfs2(v,x);
    }
}
int main()
{
    // freopen("date.in","r",stdin);
    // freopen("date.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d %d",&x,&y);
        g[x].push_back(y);
        g[y].push_back(x);
    }
    scanf("%s",s+1);
    
    dfs1(1,0);
    H2[1]=0x3f3f3f3f;
    dfs2(1,0);
    for(int i=1;i<=n;i++)
    {
        int Maxi=F2[i];
        int Seci=0;
        for(int j=0;j<g[i].size();j++)
        {
            int v=g[i][j];
            if(v==Fa[i])
            {
                continue;
            }
            int Rw=F1[v]+1;
            if(Rw>=Maxi)
            {
                Seci=Maxi;
                Maxi=Rw;
            }
            else if(Rw>=Seci)
            {
                Seci=Rw;
            }
        }
        G2[i]=Seci;
    }
    long long Res=0;
    for(int i=1;i<=n;i++)
    {
        int H=min(H1[i],H2[i]);
        if(s[i]=='1')
        {
            H=0;
        }
        int R=min(max(F1[i],F2[i]),G2[i]+2)-1;
       // printf("%d %d %d %d\n",i,H,R,H1[i]);
        if(H<=R)
        {
            Res+=(R-H+1);
        }
    }
    printf("%lld\n",Res+1);
}

posted @ 2024-01-20 10:19  kid_magic  阅读(21)  评论(3编辑  收藏  举报