BZOJ 10.29--11.7刷题总结

在总结之前,预祝2018级信奥队明天NOIP RP++

@JZYshuraK @ysy20021208 @jiangminghong @EM_LGH @zcs0724  以及我自己

开始总结题目

BZOJ1977 [BeiJing2010组队]次小生成树 Tree

BZOJ 1977[BeiJing2010组队]次小生成树 Tree

这道题我们用Kruskal把这棵最小生成树构建出来,之后我们枚举每一条非树边,也需要快速求出每一条树链上的最小边与次小边。

这个东西我们可以用倍增LCA来求。

D[x][i]=max(D[x][i-1],D[f[x][i-1]][i-1]);

D2[x][i]=max(D2[x][i-1],D2[f[x][i-1]][i-1]);

D2[x][i]=min(D[x][i-1],D[f[x][i-1]][i-1]);
D2[x][i]=max(D2[x][i],D2[x][i-1]);
D2[x][i]=max(D2[x][i],D2[f[x][i-1]][i-1]);

这几步就是求最长链与次长链。

之后就没了。

#include<cstdio>
#include<algorithm>
#define N 300010
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
int dep[N];
int f[N][20];
int head[N];
int to[N];
int nex[N];
int fa[N];
int val[N];
int idx;
int n,m;
int D[N][20];
int D2[N][20];
int size[N];
long long ans;
int minn=0x3f3f3f3f;
struct tree
{
    int x,y,z,is;
}a[N];
bool cmp(const tree &a,const tree &b){return a.z<b.z;}
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
int find(int x)
{
    if(fa[x]!=x)
        fa[x]=find(fa[x]);
    return fa[x];
}
void dfs(int x)
{
    for(int i=1;i<=17;i++)
    {
        if(dep[x]<(1<<i))
            break;
        f[x][i]=f[f[x][i-1]][i-1];
        D[x][i]=max(D[x][i-1],D[f[x][i-1]][i-1]);
        if(D[x][i-1]==D[f[x][i-1]][i-1])
            D2[x][i]=max(D2[x][i-1],D2[f[x][i-1]][i-1]);
        else
        {
            D2[x][i]=min(D[x][i-1],D[f[x][i-1]][i-1]);
            D2[x][i]=max(D2[x][i],D2[x][i-1]);
            D2[x][i]=max(D2[x][i],D2[f[x][i-1]][i-1]);
        }
    }
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=f[x][0])
        {
            dep[to[i]]=dep[x]+1;
            D[to[i]][0]=val[i];
            f[to[i]][0]=x;
            dfs(to[i]);
        }
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int deep=dep[x]-dep[y];
    for(int i=17;i>=0;i--)
        if(deep&(1<<i))
            x=f[x][i];
    for(int i=17;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    }
    if(x==y)
        return x;
    return f[x][0];
}
void merge(int x,int y)
{
    if(size[x]>size[y])
    {
        fa[y]=x;
        size[x]+=size[y];
    }
    else
    {
        fa[x]=y;
        size[y]+=size[x];    
    }
}
void go(int x,int fa,int cost)
{
    int max1=0,max2=0;
    int deep=dep[x]-dep[fa];
    for(int i=0;i<=17;i++)
    {
        if(deep&(1<<i))
        {
            if(D[x][i]>max1)
            {
                max2=max1;
                max1=D[x][i];
            }
            max2=max(max2,D2[x][i]);
            x=f[x][i];
        }
    }
    if(max1!=cost)
        minn=min(minn,cost-max1);
    else
        minn=min(minn,cost-max2);
}
void solve(int j,int cost)
{
    int x=a[j].x;
    int y=a[j].y;
    int fa=lca(x,y);
    go(x,fa,cost);
    go(y,fa,cost);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
    sort(a+1,a+m+1,cmp);
    int tot=0;
    for(int i=1;i<=m;i++)
    {
        int fx=find(a[i].x);
        int fy=find(a[i].y);
        if(fx!=fy)
        {
            tot++;
            a[i].is=1;
            ans+=a[i].z;
            merge(fx,fy);
            addedge(a[i].x,a[i].y,a[i].z);
            addedge(a[i].y,a[i].x,a[i].z);
            if(tot==n-1)
                break;
        }
    }
    dfs(1);
    for(int i=1;i<=m;i++)
        if(!a[i].is)
            solve(i,a[i].z);
    printf("%lld",ans+minn);
}

2005: [Noi2010]能量采集

2005: [Noi2010]能量采集

x=1ny=1m2×((x,y)1)+1∑x=1n∑y=1m2×((x,y)−1)+1

之后可得n×m+2x=1ny=1m(x,y)−n×m+2∑x=1n∑y=1m(x,y)

之后我们直接求解。

#include<stdio.h>
long long min(long long x,long long y)
{
    if(x>y)
        return y;
    return x;
}
long long n,m;
long long f[100001];
int main()
{
    scanf("%lld%lld",&n,&m);
    long long ans2=min(n,m),ans=0;
    for(long long i=ans2;i>=1;i--)
    {
        f[i]=(m/i)*(n/i);
        for(long long j=2*i;j<=ans2;j+=i)
            f[i]-=f[j];
        ans+=f[i]*(2*i-1);
    }
    printf("%lld",ans);
}

2007: [Noi2010]海拔

2007: [Noi2010]海拔

和狼抓兔子有点像,每一点的高度只能为0或1.平面图转对偶图跑最短路。

#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 1500002
int S=0,T=1500001;
int idx;
int head[N];
int to[N];
int val[N];
int nex[N];
long long f[N];
int inq[N];
int x;
int n;
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
int arr(int x,int y)
{
    if(x==0||y>n)
        return S;
    if(x>n||y==0)
        return T;
    return (x-1)*n+y;
}
struct Point
{
    int dis,number;
    inline bool operator < (const Point &a) const
    {
        return dis>a.dis;
    }
};
priority_queue < Point > q;
void dijkstra(int S)
{
    Point tmp;
    tmp.number=S;
    tmp.dis=0;
    for(int i=1;i<N;i++)
        f[i]=1e16;
    f[tmp.number]=0;
    q.push(tmp);
    while(!q.empty())
    {
        int x=q.top().number;
        q.pop();
        if(inq[x])
            continue;
        inq[x]=0;
        for(int i=head[x];i;i=nex[i])
        {
            if(f[to[i]]>f[x]+val[i]&&(!inq[to[i]]))
            {
                f[to[i]]=f[x]+val[i];
                tmp.dis=f[to[i]];
                tmp.number=to[i];
                q.push(tmp);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&x);
            addedge(arr(i-1,j),arr(i,j),x);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
        {
            scanf("%d",&x);
            addedge(arr(i,j),arr(i,j-1),x);
        }
    for(int i=1;i<=n+1;i++)
        for(int j=1;j<=n;j++)
        {
            scanf("%d",&x);
            addedge(arr(i,j),arr(i-1,j),x);
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n+1;j++)
        {
            scanf("%d",&x);
            addedge(arr(i,j-1),arr(i,j),x);
        }
    dijkstra(S);
    printf("%lld",f[T]);
    return 0;
}

1079: [SCOI2008]着色方案

1079: [SCOI2008]着色方案

guangheli的考试题,10分部分分,我直接爆蛋。

K和Ci都比较小,我们考虑用记忆化搜索。

Ci为5,所以我们可以把它存进一个桶里,之后6维DP

f[a][b][c][d][e][last]表示每种颜色的个数以及最后一个数的颜色。

#include<cstdio>
long long f[16][16][16][16][16][6];
long long t[16];
long long mod=1e9+7;
int n,x;
long long dfs(long long a,long long b,long long c,long long d,long long e,long long last)
{
    if(f[a][b][c][d][e][last]!=0)
        return f[a][b][c][d][e][last];
    if(a+b+c+d+e==0)
        return 1;
    long long ans=0;
    if(a!=0)
        ans+=(a-(last==2))*dfs(a-1,b,c,d,e,1);
    if(b!=0)
        ans+=(b-(last==3))*dfs(a+1,b-1,c,d,e,2);
    if(c!=0)
        ans+=(c-(last==4))*dfs(a,b+1,c-1,d,e,3);
    if(d!=0)
        ans+=(d-(last==5))*dfs(a,b,c+1,d-1,e,4);
    if(e!=0)
        ans+=e*dfs(a,b,c,d+1,e-1,5);
    f[a][b][c][d][e][last]=ans%mod;
    return f[a][b][c][d][e][last];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        t[x]++;
    }
    printf("%lld",dfs(t[1],t[2],t[3],t[4],t[5],0));
}

BZOJ 4326: NOIP2015 运输计划

把一条边置成0,问最长链的最小值。

我们可以用倍增LCA构建这棵树,之后差分数组+二分答案解决此题。

我校OJ卡常,我也没喆

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 300010
int fa[N][20];
int head[N];
int to[N*2];
int val[N*2];
int nex[N*2];
int n,m;
long long dis[N];
long long length[N];
int lu[N],lv[N];
long long sum;
int a,b,c;
int lca[N];
long long mid;
int f[N];
int dep[N];
int idx;
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
void dfs(int x,int f)
{
    dep[x]=dep[f]+1;
    fa[x][0]=f;
    for(int i=head[x];i;i=nex[i])
        if(to[i]!=f)
        {
            dis[to[i]]=dis[x]+val[i];
            dfs(to[i],x);
        }
}
int Lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int deep=dep[x]-dep[y];
    for(int i=19;i>=0;i--)
        if(deep&(1<<i))
            x=fa[x][i];
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    if(x==y)
        return x;
    return fa[x][0];
}
void dfs2(int x,int fa)
{
    for(int i=head[x];i;i=nex[i])
        if(to[i]!=fa)
        {
            dfs2(to[i],x);
            f[x]+=f[to[i]];
        }
}
bool check(int x)
{
    int many=0;
    for(int i=1;i<=n;i++)
        f[i]=0;
    for(int i=1;i<=m;i++)
        if(length[i]>mid)
        {
            many++;
            f[lu[i]]++;
            f[lv[i]]++;
            f[lca[i]]-=2;
        }
    dfs2(1,0);
    for(int i=2;i<=n;i++)
        if(f[i]>=many&&sum-dis[i]+dis[fa[i][0]]<=mid)
            return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        addedge(b,a,c);
        addedge(a,b,c);
    }
    dfs(1,0);
    for(int j=1;j<=19;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&lu[i],&lv[i]);
        lca[i]=Lca(lu[i],lv[i]);
        length[i]=dis[lu[i]]+dis[lv[i]]-2*dis[lca[i]];
        sum=max(sum,length[i]);
    }
    long long l=0,r=sum+1;
    while(l<r)
    {
        mid=(l+r)>>1;
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    printf("%lld",l);
}

2190: [SDOI2008]仪仗队

2190: [SDOI2008]仪仗队

大水题,线筛欧拉函数不要写错哦!

#include<cstdio>
#define N 500010
int n;
int phi[N];
bool notprime[N];
int prime[N];
int idx;
long long ans;
int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        if(!notprime[i])
            prime[++idx]=i,phi[i]=i-1;
        for(int j=1;j<=idx&&i*prime[j]<=n;j++)
        {
            notprime[i*prime[j]]=1;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
    }
    for(int i=2;i<n;i++)
        ans+=phi[i];
    if(n==1)
    {
        printf("0");
        return 0;
    }
    printf("%lld",ans*2+3);
}

1040: [ZJOI2008]骑士

1040: [ZJOI2008]骑士

N个点,N条边显然是个基环树。我们要拆掉所有环之后进行DP

f[i]表示选择i点及子树最大值

g[i]表示不选择i点及子树最大值

最后的答案就是那些环中点g[i]的最大值

#include<cstdio>
#define max(a,b) ((a) > (b) ? (a) : (b))
#define N 2000100
int n;
long long fight[N];
int fa[N];
int a;
int head[N];
int to[N];
int nex[N];
int idx;
int root1,root2;
long long f[N];
long long g[N];
long long ans,ans2;
int lu[N];
int lv[N];
int tot;
void addedge(int a,int b)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
}
int find(int x)
{
    if(fa[x]!=x)
        fa[x]=find(fa[x]);
    return fa[x];
}
void dfs(int x,int fa)
{
    f[x]=fight[x];
    g[x]=0;
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa)
        {
            dfs(to[i],x);
            g[x]+=max(f[to[i]],g[to[i]]);
            f[x]+=g[to[i]];
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld%d",&fight[i],&a);
        int fx=find(i);
        int fy=find(a);
        if(fx!=fy)
        {
            addedge(i,a);
            addedge(a,i);
            fa[fx]=fy;
        }
        else
        {
            lu[++tot]=a;
            lv[tot]=i;
        }
    }
    for(int i=1;i<=tot;i++)
    {
        dfs(lu[i],0);
        ans2=g[lu[i]];
        dfs(lv[i],0);
        ans2=max(g[lv[i]],ans2);
        ans+=ans2;
    }
    printf("%lld",ans);
}

BZOJ 2173 整数的lqp拆分

jiangminghong的考试题,好题就被我手动打表找规律废掉了

ans[i]=*ans[i-1]+ans[i-2]

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 3000100
int mod=1e9+7;
int n;
long long ans[N];
int main()
{
    scanf("%d",&n); 
    ans[1]=1,ans[2]=2,ans[3]=5;
    for(int i=4;i<=n;i++)
        ans[i]=((ans[i-1]*2)%mod+ans[i-2])%mod;
    printf("%lld",ans[n]);
    return 0;
}

BZOJ 1131 [POI2008]Sta

BZOJ 1131 [POI2008]Sta

我又想起了llq说的扭一扭,这道题就是这么更新的。

我们先DFS扫一遍,之后对于每个点将其扭到根,它子树的dep都减去1,其他点的dep都加上1.

之后我们O(n)解决!

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 2000010
int n;
int nex[N];
int to[N];
int dep[N];
int size[N];
int head[N];
long long ans[N];
long long ans2;
int idx;
int a,b,ans3;
void addedge(int a,int b)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
}
void dfs(int x,int fa)
{
    size[x]=1;
    dep[x]=dep[fa]+1;
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa)
        {
            dfs(to[i],x);
            size[x]+=size[to[i]];
        }
    }
}
void pose(int x,int fa)
{
    ans[x]=ans[fa]-size[x]+(n-size[x]);
    for(int i=head[x];i;i=nex[i])
        if(to[i]!=fa)
            pose(to[i],x);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        addedge(a,b);
        addedge(b,a);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
        ans[1]+=dep[i];
    pose(1,1);
    for(int i=1;i<=n;i++)
        if(ans2<ans[i])
        {
            ans2=ans[i];
            ans3=i;
        }
    printf("%d",ans3);
}

2208: [Jsoi2010]连通数

2208: [Jsoi2010]连通数

JZYshuraK的好想法,刚开始我一眼tarjan,JZYshuraK告诉我直接floyd让我直接蒙圈。

他说闭包传递+bitset优化可解决。2*108过掉,码量超短。

#include <cstdio>
#include<bitset>
#include<algorithm>
using namespace std;
bitset<2010> is[2010];
char s[2010][2010];
int ans=0;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1); 
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) 
            if(s[i][j]=='1'||i==j)
                is[i][j]=true;
    for(int j=1;j<=n;j++)
        for(int i=1;i<=n;i++)
            if(is[i][j])
                is[i]|=is[j];
     
    for(int i=1;i<=n;i++)
        ans+=is[i].count();
    printf("%d",ans);
    return 0;
}

1060: [ZJOI2007]时态同步

1060: [ZJOI2007]时态同步

f[i]表示距离i最远点的距离,之后直接裸树形DP

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1000001
int n,S;
int head[N];
int to[N];
int nex[N];
long long val[N];
long long f[N];
int a,b;
long long c;
int idx;
long long ans;
void addedge(int a,int b,long long c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
void dfs(int x,int fa)
{
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa)
        {
            dfs(to[i],x);
            f[x]=max(f[to[i]]+val[i],f[x]);
        }
    }
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa)
        {
            ans=ans+f[x]-val[i]-f[to[i]];
        }
    }
}
int main()
{
    scanf("%d",&n);
    scanf("%d",&S);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%lld",&a,&b,&c);
        addedge(a,b,c);
        addedge(b,a,c);
    }
    dfs(S,0);
    printf("%lld",ans);
    return 0;
}

BZOJ 1077 天平

1077 [SCOI2008]天平

floyd跑差分约束,我刚开始还写WA了。

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 501
int n,A,B;
char s[101][101];
int mx[501][501];
int mn[501][501];
int c1,c2,c3;
int main()
{
    scanf("%d%d%d",&n,&A,&B);
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(s[i][j]=='+')
            {
                mn[i][j]=1;
                mx[i][j]=2;
            }
            else if(s[i][j]=='-')
            {
                mn[i][j]=-2;
                mx[i][j]=-1;
            }
            else if(s[i][j]=='?')
            {
                mn[i][j]=-2;
                mx[i][j]=2;
            }
            else
            {
                mn[i][j]=0;
                mx[i][j]=0;             
            }
        }
        mn[i][i]=0;
        mx[i][i]=0;
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                if(i==j||i==k||j==k)
                    continue;
                mn[i][j]=max(mn[i][j],mn[i][k]+mn[k][j]);
                mx[i][j]=min(mx[i][j],mx[i][k]+mx[k][j]);
            }
    for(int i=1;i<=n;i++)
    {
        if(i==A||i==B)
            continue;
        for(int j=i+1;j<=n;j++)
        {
            if(j==A||j==B)
                continue;
            if(mn[A][i]>mx[j][B]||mn[A][j]>mx[i][B])
                c1++;
            if(mx[A][i]<mn[j][B]||mx[A][j]<mn[i][B])
                c3++;
            if((mn[A][i]==mx[A][i]&&mn[B][j]==mx[B][j]&&mn[A][i]+mx[B][j]==0)||(mn[A][j]==mx[A][j]&&mn[B][i]==mx[B][i]&&mn[A][j]+mx[B][i]==0))
                c2++;
        }
    }
    printf("%d %d %d",c1,c2,c3);
    return 0;
}

 BZOJ 5165 树上倍增

听ysy20021208说是JLOI 2018 D2 T2佛光树改了一个数据范围

这题直接新建节点跑LCA就OK了

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 3000001
int n,m,K;
int f[N][21];
int dep[N];
char opt[2];
int idx=1;
int x,y;
int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int deep=dep[x]-dep[y];
    for(int i=20;i>=0;i--)
        if(deep&(1<<i))
            x=f[x][i];
    for(int i=20;i>=0;i--)
        if(f[x][i]!=f[y][i])
            x=f[x][i],y=f[y][i];
    if(x==y)
        return x;
    return f[x][0];
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%s",opt);
        if(opt[0]=='A')
        {
            scanf("%d",&x);
            dep[++idx]=dep[x]+1;
            f[idx][0]=x;
            for(int i=1;i<=20;i++)
            {
                if(!f[idx][i-1])
                    break;
                f[idx][i]=f[f[idx][i-1]][i-1];
            }
        }
        if(opt[0]=='Q')
        {
            scanf("%d",&K);
            K--;
            scanf("%d",&x);
            while(K--)
            {
                scanf("%d",&y);
                if(x!=1)
                    x=lca(x,y);
            }
            printf("%d
",x);
        }
    }
}

BZOJ 2705 Longge的问题

欧拉函数好题

从老师PPT直接摘下

#include<cstdio>
typedef long long ll;
ll n;
ll ans;
ll oula(ll n)
{
    ll ans=n;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)
                n/=i;
        }
    }
    if(n>1)
        ans=ans/n*(n-1);
    return ans;
}
int main()
{
    scanf("%lld",&n);
    for(int i=1;i*i<=n;i++)
        if(n%i==0)
        {
            ans+=oula(n/i)*i;
            if(i*i<n)
                ans+=(n/i)*oula(i);
        }
    printf("%lld",ans);
    return 0;
}

BZOJ 4800 [Ceoi2015]Ice Hockey World Championship

n<=40,一眼 Meet in the middle,分成两个序列之后二分

#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
ll n;
ll m;
ll a[1<<22];
ll b[1<<22];
ll idx1,idx2;
ll val[44];
ll ans;
void dfs(ll dep,ll n,ll m,ll sum)
{
    if(sum>m)
        return ;
    if(dep>n)
    {
        a[++idx1]=sum;
        return ;
    }
    dfs(dep+1,n,m,sum);
    dfs(dep+1,n,m,sum+val[dep]);
}
void dfs2(ll dep,ll n,ll m,ll sum)
{
    if(sum>m)
        return ;
    if(dep>n)
    {
        b[++idx2]=sum;
        return ;
    }
    dfs2(dep+1,n,m,sum);
    dfs2(dep+1,n,m,sum+val[dep]);
}
int main()
{
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&val[i]);
    dfs(1,n>>1,m,0);
    dfs2((n>>1)+1,n,m,0);
    sort(a+1,a+idx1+1);
    sort(b+1,b+idx2+1);
    for(ll i=1;i<=idx2;i++)
    {
        if(m-b[i]>=a[idx1])
            ans+=idx1;
        else
            ans+=upper_bound(a+1,a+idx1+1,m-b[i])-a-1;        
    }
    printf("%lld",ans);
}

BZOJ 1430 小猴打架

通过这道题我学会了一个新知识点--Prufer序列

显然,对于N个节点的生成树,种类有N^(N-2),因为这道题对与打架顺序还要重复计算,所以再来个N!/N,也即是(N-1)!,所以F(x)=(x-1)!*x^(x-2)

#include<cstdio>
typedef long long ll;
ll mod=9999991;
ll n,ans=1;
int main()
{
    scanf("%lld",&n);
    for(int i=1;i<=n-2;i++)
        ans=(ans*n)%mod;
    for(int i=2;i<n;i++)
        ans=(ans*i)%mod;
    printf("%lld",ans);
}

BZOJ 4173 数学

结果是phi(n)*phi(m)*n*m

#include<cstdio>
typedef long long ll;
ll mod=998244353;
ll n,m;
ll ans;
ll phi(ll n)
{
    ll ans=n;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            ans=ans/i*(i-1);
            while(n%i==0)
                n/=i;
        }
    }
    if(n!=1)
        ans=ans/n*(n-1);
    return ans%mod;
}
int main()
{
    scanf("%lld%lld",&n,&m);
    ans=phi(n)*phi(m)%mod*(n%mod)%mod*(m%mod)%mod;
    printf("%lld",ans);
    return 0;
}

BZOJ 3517 翻硬币

好题一道,刚开始一眼高斯消元解异或方程组。后来才看出来

设is[i][j]为此状态翻不翻。决定这个点只有这一行与这一列的情况,我们求一个前缀异或值,倒过来就是1的状态

#include<cstdio>
int n;
int a[1001][1001];
int is[1001][1001];
int row[1001];
int column[1001];
int ans;
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            scanf("%1d",&a[i][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            row[i]^=a[i][j];
            column[j]^=a[i][j];
        }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            int tmp=row[i]^column[j];
            tmp^=a[i][j];
            ans+=tmp;
        }
    if(ans>n*n-ans)
        ans=n*n-ans;
    printf("%d",ans);
}

BZOJ 2438: [中山市选2011]杀人游戏

tarjan缩点+判断,有坑。最后一个缩块入度为0并且只有一个点答案-1

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 300010
int tot;
int nex[N<<1];
int to[N<<1];
int head[N<<1];
int dep[N];
int low[N];
int blob[N];
int stack[N];
int vis[N];
int inz[N];
int top;
int cnt,idx;
int sum;
int in[N];
int inblob[N];
int n,m;
int x,y;
int ans;
void addedge(int a,int b)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
}
void tarjan(int x)
{
    dep[x]=low[x]=++cnt;
    stack[++top]=x;
    vis[x]=inz[x]=1;
    for(int i=head[x];i;i=nex[i])
    {
        if(!vis[to[i]])
        {
            tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }
        else if(inz[to[i]])
            low[x]=min(low[x],dep[to[i]]);
    }
    if(dep[x]==low[x])
    {
        int here;
        sum++;
        do
        {
            here=stack[top--];
            blob[here]=sum;
            inz[here]=0;
            in[sum]++;
        }while(here!=x);
    }
}
bool is(int x)
{
    int i;
    bool flag=1;
    for(i=head[x];i;i=nex[i])
        inblob[blob[to[i]]]--;
    for(i=head[x];i;i=nex[i])
        if(!inblob[blob[to[i]]])
            flag=0;
    for(i=head[x];i;i=nex[i])
        inblob[blob[to[i]]]++;
    return flag;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    for(int i=1;i<=n;i++)
        if(!vis[i])
            tarjan(i);
    for(int x=1;x<=n;x++)
        for(int i=head[x];i;i=nex[i])
            if(blob[x]!=blob[to[i]])
                inblob[blob[to[i]]]++;
    for(int i=1;i<=sum;i++)
        if(!inblob[i])
            ans++;
    int i;
    for(i=1;i<=n;i++)
        if(in[blob[i]]==1&&!inblob[blob[i]]&&is(i))
            break;
    if(i<=n)
        ans--;
    printf("%.6lf
",(double)(n-ans)/n);
    return 0;
}

BZOJ 5085 最大

这题思维难度较大,二分+枚举线段

总时间O(n2logmaxlongint)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int boom[1001][1001];
int a[1001][1001];
int tot,cnt[1001];
int n,m;
int l=0x7f7f7f7f,r=0,mid;
int ans;
bool check(int mid)
{
    memset(boom,0,sizeof(boom));
    for(int i=1;i<=n;i++)
    {
        tot=0;
        for(int j=1;j<=m;j++)
            if(a[i][j]>=mid)
                cnt[++tot]=j;
        for(int j=1;j<=tot;j++)
            for(int k=j+1;k<=tot;k++)
            {
                if(boom[cnt[j]][cnt[k]]==1)
                    return 1;
                boom[cnt[j]][cnt[k]]=1;
            }
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&a[i][j]);
            l=min(l,a[i][j]);
            r=max(r,a[i][j]);
        }
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(check(mid))
            l=mid+1,ans=mid;
        else
            r=mid;
    }
    printf("%d",ans);
    return 0;
}

BZOJ 1954 The Xor-longest Path

Trie树题目,dfs边权时变成异或

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1500010
int n;
int a,b,c;
int head[N];
int to[2*N];
int val[2*N];
int nex[2*N];
int ch[N][2];
int dis[N];
int cnt;
int ans;
int idx;
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
void trie(int x)
{
    int here=0;
    bool is;
    for(int i=30;i>=0;i--)
    {
        if(x&(1<<i))
            is=1;
        else
            is=0;
        if(!ch[here][is])
            ch[here][is]=++cnt;
        here=ch[here][is];
    }
}
void dfs(int x,int fa)
{
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa)
        {
            dis[to[i]]=dis[x]^val[i];
            dfs(to[i],x);
        }
    }
}
int find(int x)
{
    int here=0,ans=0;
    for(int i=30;i>=0;i--)
    {
        int is=x&(1<<i)?1:0;
        if(ch[here][!is])
            ans|=(1<<i),here=ch[here][!is];
        else
            here=ch[here][is];
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&a,&b,&c);
        addedge(a,b,c);
        addedge(b,a,c);
    }
    dfs(1,0);
    for(int i=1;i<=n;i++)
    {
        trie(dis[i]);
        ans=max(ans,find(dis[i]));
    }
    printf("%d",ans);
}

BZOJ 3251树上三角形

总共fib数列也没几个数,直接暴力LCA更新就OK

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1000100
int n,Q;
int val[N];
int fa[N];
int a,b;
int head[N];
int to[N];
int nex[N];
int dep[N];
int idx=1;
int ans[N];
int tot;
void addedge(int a,int b)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
}
void dfs(int x)
{
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa[x])
        {
            dep[to[i]]=dep[x]+1;
            dfs(to[i]);
        }
    }
}
bool go(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    tot=0;
    while(dep[x]>dep[y])
    {
        ans[++tot]=val[x];
        x=fa[x];
        if(tot>50)
            return 1;
    }
    while(x!=y)
    {
        ans[++tot]=val[x];
        ans[++tot]=val[y];
        x=fa[x];
        y=fa[y];
        if(tot>50)
            return 1;
    }
    ans[++tot]=val[x];
    sort(ans+1,ans+tot+1);
    for(int i=3;i<=tot;i++)
    {
        if(ans[i]-ans[i-2]<ans[i-1])
            return 1;
    }
    return 0;
}
int main()
{
    scanf("%d%d",&n,&Q);
    for(int i=1;i<=n;i++)
        scanf("%d",&val[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&a,&b);
        addedge(a,b);
        fa[b]=a;
    }
    dfs(1);
    while(Q--)
    {
        int opt;
        scanf("%d",&opt);
        if(opt==0)
        {
            scanf("%d%d",&a,&b);
            if(go(a,b))
                puts("Y");
            else
                puts("N");
        }
        else
        {
            scanf("%d%d",&a,&b);
            val[a]=b;
        }
    }
}

BZOJ 2523 聪明的学生

强大的题目,贼神奇,其实是个迭代的过程

#include<cstdio>
using namespace std;
int n,m,last[3]={2,0,1},nex[3]={1,2,0},ans[30005][3],Ans,num;
void work(int x,int y,int t)
{
    if(Ans>n)
        return;
    if(x==y)
    {
        Ans+=t+1;
        return;
    }
    if(x>y)
    {
        Ans+=2;
        work(y,x-y,nex[t]);
    }
    else
    {
        Ans++;
        work(y-x,x,last[t]);
    }
}
int main()
{
    while(scanf("%d%d",&n,&m)&&n!=-1)
    {
        num=0;
        for(int i=1;i<m;i++)
        {
            int j=m-i;
            Ans=0;
            work(i,j,(n-1)%3);
            if(Ans==n)
                ans[++num][nex[(n-1)%3]]=i,ans[num][(n-1)%3]=m,ans[num][last[(n-1)%3]]=j;
        }
        printf("%d
",num);
        if((n-1)%3==1)
            for(int i=num;i>0;i--)
                printf("%d %d %d
",ans[i][0],ans[i][1],ans[i][2]);
        else
            for(int i=1;i<=num;i++)
                printf("%d %d %d
",ans[i][0],ans[i][1],ans[i][2]);
    }
    return 0;
}

BZOJ 4668 冷战

并查集路径压缩+启发式合并&LCA

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 1000100
int n,m;
int ans[N];
int fa[N];
int size[N];
int idx;
int lastans;
int ti[N];
int dep[N];
int tim=0;
int x,y;
int find(int x)
{
    int f;
    if(fa[x]==x)
        return fa[x];
    else
    {
        f=find(fa[x]);
        dep[x]=dep[fa[x]]+1;
    }
    return f;
}
void merge(int x,int y,int i)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
    {
        if(size[fx]>size[fy])
            swap(fx,fy);    
        fa[fx]=fy;
        size[fy]+=fx;
        ti[fx]=i;
    }
}
int lca(int x,int y)
{
    int fx=find(x);
    int fy=find(y);
    if(fx!=fy)
        return 0;
    int ans=0; 
    if(dep[x]<dep[y])
        swap(x,y);
    while(dep[x]>dep[y])
    {
        ans=max(ans,ti[x]);
        x=fa[x];
    }
    while(x!=y)
    {
        ans=max(ans,ti[x]);
        ans=max(ans,ti[y]);
        x=fa[x];
        y=fa[y];
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        fa[i]=i,size[i]=1;
    int opt;
    for(int i=1;i<=m;i++)
    {   
        scanf("%d",&opt);
        if(!opt)
        {
            scanf("%d%d",&x,&y);
            x=lastans^x;
            y=lastans^y;
            merge(x,y,++tim);
        }
        else
        {
            scanf("%d%d",&x,&y);
            x=lastans^x;
            y=lastans^y;
            int ans=lca(x,y);
            printf("%d
",ans);
            lastans=ans;            
        }
    }
    return 0;
}

BZOJ 3252: 攻略

ysy20021208的考试题。

我们可以长链剖分直接解决这个问题,取K条最长链

#include<cstdio>
#include<algorithm>
using namespace std;
#define N 500010
int to[N];
int head[N];
int nex[N];
int son[N];
long long dep[N];
int a[N];
int fa[N];
long long mx[N];
int n,K;
int x,y;
int idx;
long long lian[N];
int que[N];
int cnt;
long long ans;
bool cmp(int x,int y)
{
    return lian[x]>lian[y];
}
void addedge(int a,int b)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
}
void dfs(int x,int f)
{
    dep[x]=dep[f]+a[x];
    fa[x]=f;
    mx[x]=dep[x];
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa[x])
        {
            dfs(to[i],x);
            mx[x]=max(mx[x],mx[to[i]]);
            if(mx[to[i]]>mx[son[x]])
                son[x]=to[i];
        }
    }
}
void dfs2(int x,int t)
{
    lian[t]+=a[x];
    if(son[x])
        dfs2(son[x],t);
    for(int i=head[x];i;i=nex[i])
    {
        if(to[i]!=fa[x]&&to[i]!=son[x])
        {
            que[++cnt]=to[i];
            dfs2(to[i],to[i]);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    dfs(1,0);
    que[++cnt]=1;
    dfs2(1,1);
    sort(que+1,que+cnt+1,cmp);
    for(int i=1;i<=K;i++)
        ans+=lian[que[i]];
    printf("%lld",ans);
}

BZOJ 1452 JSOI2009 Count

二维树状数组直接解决修改&查询问题

#include<cstdio>
int f[301][301][101];
int map[301][301];
int n,m;
int x,y,c;
int x1,x2,y1,y2;
int lowbit(int x){return x&-x;}
void update(int x,int y,int val,int delta)
{
    for(int i=x;i<=n;i+=lowbit(i))
        for(int j=y;j<=m;j+=lowbit(j))
            f[i][j][val]+=delta;
}
int query(int x,int y,int val)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i))
        for(int j=y;j;j-=lowbit(j))
            ans+=f[i][j][val];
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            scanf("%d",&map[i][j]);
            update(i,j,map[i][j],1);
        }
    int Q,opt;
    scanf("%d",&Q);
    while(Q--)
    {
        int ans=0;
        scanf("%d",&opt);
        if(opt==1)
        {
            scanf("%d%d%d",&x,&y,&c);
            update(x,y,map[x][y],-1);
            map[x][y]=c;
            update(x,y,c,1);
        }
        else
        {
            scanf("%d%d%d%d%d",&x1,&x2,&y1,&y2,&c);
            ans=ans+query(x2,y2,c)-query(x1-1,y2,c)-query(x2,y1-1,c)+query(x1-1,y1-1,c);
            printf("%d
",ans);
        }
    }
}

BZOJ 1485 有趣的数列

和括号序一样的Catlan数。

之后不让直接求md

#include<cstdio>
#define N 2000001
long long n,P;
long long prime[N];
bool notprime[N];
int idx;
long long pos[N];
long long f[N];
long long ans=1;
void change(int x,int delta)
{
    while(x!=1)
    {
        f[prime[pos[x]]]+=delta;
        x/=prime[pos[x]];
    }
}
long long pow(long long x,long long y)
{
    long long ans=1;
    while(y)
    {
        if(y&1)
            ans=(ans*x)%P;
        x=(x*x)%P;
        y>>=1;
    }
    return ans;
}
int main()
{
    scanf("%lld%lld",&n,&P);
    for(int i=2;i<=2*n;i++)
    {
        if(!notprime[i])
            prime[++idx]=i,pos[i]=idx;
        for(int j=1;j<=idx&&i*prime[j]<=2*n;j++)
        {
            notprime[i*prime[j]]=1;
            pos[i*prime[j]]=j;
            if(i%prime[j]==0)
                break;
        }
    }
    for(int i=n+2;i<=2*n;i++)
        change(i,1);
    for(int i=1;i<=n;i++)
        change(i,-1);
    for(int i=1;i<=idx;i++)
        if(f[prime[i]])
            ans=ans*pow(prime[i],f[prime[i]])%P;
    printf("%lld",ans%P);
}

BZOJ 2259新型计算机

刚开始就想最短路了,但是只拿了50分暴力

100分的建图非常神奇了

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<queue>
#include<cstring>
using namespace std;
#define N 4000100
int n;
int x;
int head[N];
int to[N];
int val[N];
int nex[N];
int f[N];
int vis[N];
int vis2[N];
int idx;
void addedge(int a,int b,int c)
{
    nex[++idx]=head[a];
    head[a]=idx;
    to[idx]=b;
    val[idx]=c;
}
struct Point
{
    int number,dis;
    inline bool operator < (const Point &a)const
    {
        return dis>a.dis;
    }
};
priority_queue <Point> q;
void dijkstra(int S)
{
    memset(f,0x3f,sizeof(f));
    Point tmp;
    tmp.number=S;
    tmp.dis=0;
    f[S]=0;
    q.push(tmp);
    while(!q.empty())
    {
        int x=q.top().number;
        q.pop();
        for(int i=head[x];i;i=nex[i])
        {
            if(f[to[i]]>f[x]+val[i])
            {
                f[to[i]]=f[x]+val[i];
                tmp.dis=f[to[i]];
                tmp.number=to[i];
                q.push(tmp);
            }
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        for(int j=i+1;j<=min(i+x+1,n)&&(!vis[j]);j++)
            vis[j]=1,addedge(j,j-1,1);
        for(int j=i+x+1;j<=n&&!vis2[j];j++)
            vis2[j]=1,addedge(j,j+1,1);
        if(i+x+1<=n+1)
            addedge(i,i+x+1,0);
        else
            addedge(i,n+1,i+x-n);
    }
    dijkstra(1);
    printf("%d",f[n+1]);
    return 0;
}

BZOJ 1047 理想的正方形

二维RMQ解决,并不是单调队列

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define N 1010
int f[11][N][N],f2[11][N][N];
int ans=2e9;
int map[N][N];
int minn,maxn;
int a,b,n;
int k;
int main()
{
    scanf("%d%d%d",&a,&b,&n);
    for(int i=1;i<=a;i++)
        for(int j=1;j<=b;j++)
        {
            scanf("%d",&map[i][j]);
            f[0][i][j]=f2[0][i][j]=map[i][j];
        }
    for(k=1;(1<<k)<=n;k++)
        for(int i=1;i<=a-(1<<k)+1;i++)
            for(int j=1;j<=b-(1<<k)+1;j++)
            {
                f[k][i][j]=max(max(f[k-1][i+(1<<(k-1))][j],f[k-1][i][j]),max(f[k-1][i][j+(1<<(k-1))],f[k-1][i+(1<<(k-1))][j+(1<<(k-1))]));
                f2[k][i][j]=min(min(f2[k-1][i+(1<<(k-1))][j],f2[k-1][i][j]),min(f2[k-1][i][j+(1<<(k-1))],f2[k-1][i+(1<<(k-1))][j+(1<<(k-1))]));
            }
    for(int i=1;i<=a-n+1;i++)
        for(int j=1;j<=b-n+1;j++)
        {
            maxn=max(max(f[k-1][i][j],f[k-1][i][j+n-(1<<(k-1))]),max(f[k-1][i+n-(1<<(k-1))][j],f[k-1][i+n-(1<<(k-1))][j+n-(1<<(k-1))]));
            minn=min(min(f2[k-1][i][j],f2[k-1][i][j+n-(1<<(k-1))]),min(f2[k-1][i+n-(1<<(k-1))][j],f2[k-1][i+n-(1<<(k-1))][j+n-(1<<(k-1))]));
            ans=min(ans,maxn-minn);
        }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/342zhuyongqi/p/9935077.html