呜呜呜,这场爆0了,过于渴望T1正解导致暴力。。。

T1

一个数学题,用n^4的方法枚举两个点然后去重,可以拿到40分

我们再看n^2的做法,首先,平行于坐标轴的直线有n+m条,任意一条斜率为正且不垂直于坐标轴都可以把斜率取反,对应另一条直线

所以我们只考虑斜率为正且不垂直于坐标轴的直线。

考虑枚举方向向量a,b显然有gcd(a,b)=1,我们定义一个点(x,y)的前驱后继分别为(x-a,y-b),(x+a,y+b),

则直线的数量满足它的前驱和自己在这个矩阵内,但他的后继不在的点的数量

然后我们经过大力计算发现式子为

(n-a)(m-b)-max(n-2a,0)(m-2b,0);

那么ans就等于

sigma(i=1,i<n)sigma(j=1,j<m)(gcd( i , j )==1)*(n-a)(m-b)-max(n-2a,0)(m-2b,0);

由于官方给的二维前缀和我不太懂,所以这里给出另一种解法

我们发现只有在gcd(i,j)=1的情况会作出贡献,那么我们定义两个数组

num[ i ][ j ]表示从1到j与i互质的数的个数,sum[ i ][ j ]表示从1到j与i互质的数的和

那么ans=sigma(i=1,i<n)(num[ i ][ m-1 ]*m-sum[ i ][ m-1 ])*( n-i )-( 2*i<n )( n-2*i )*(num[ i ][ m/2 ]*m-2*sum[ i ][ m/2 ])

处理的时候可以数组模拟辗转相除,把预处理的n^2log(n)降低成n^2

那么总时间复杂度就是n^2+n*T;

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
inline int abs(int x){return x<0?-x:x;}
inline int gcd(int a,int b){return b?gcd(b,a%b):a;}
inline int lcm(int x,int y){return x/gcd(x,y)*y;}
const int maxn=4001;
const int mod=1<<30;
int ans=0;
int sum[maxn+1][maxn+1];
short ggcd[maxn+1][maxn+1],g[maxn+1][maxn+1];
signed main()
{
    //freopen("out","w",stdout);
    for(int i=1;i<=maxn;i++)
    {
        g[i][i]=g[i][0]=g[0][i]=i;
        for(int j=1;j<i;j++)
        g[i][j]=g[j][i]=g[j][i%j];
    }
    for(int i=1;i<=maxn;i++)
    {
        for(int j=1;j<=maxn;j++)
        {
            ggcd[i][j]=ggcd[i][j-1];
            sum[i][j]=sum[i][j-1];
            if(g[i][j]==1)
            ggcd[i][j]++,sum[i][j]+=j;
        }
    }
    int t=read();
    while(t--)
    {
        long long ans=0;
        int n=read();
        int m=read();
        for(int i=1;i<n;i++)
        {
            (ans+=((long long)ggcd[i][m-1]*m%mod-sum[i][m-1]+mod)*(n-i))%=mod;
            if(2*i<n)
            ((ans-=(long long)(n-i*2)*(ggcd[i][m/2]*m%mod-2*sum[i][m/2]%mod))+=mod)%=mod;
        }
        printf("%lld\n",(ans*2+n+m+mod)%mod);
    }
}
T1

T2影子

这题树剖暴力有35,犯了个智障错误爆0了;

说下正解:

将所有点按照权值从大到小排序,对于将当前点和与其相连的所有点依次合

并到一个集合中。并查集需要维护当前集合中的最长路径长度和对应的两个端点。

在合并两个集合后,最终集合的最长路一定只有两类情况:一类是其中一个

集合的最长路,一共有 2 种;一类是由两个集合的最长路的端点互相连接而成,

一共有 2×2=4 种。需要用到最近公共祖先的算法预处理求两点在树上的距离,

离线处理即可。每次合并并查集之后用当前点的权值乘以最长路的总长度来更新

最优结果即可。即使这个点不在当前合并后的集合的最长路上也是没有问题的,

因为如果这样的话,必然已经在之前得到了对应的结果,这次合并不会对最终结

果产生影响。

复杂度。。不会证明

#include<bits/stdc++.h>
#define jb cout<<"jb";
#define int long long
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int maxn=1e5+1;
int cnt,num,n;
int w[maxn],dis[maxn],head[maxn],to[maxn*2],val[maxn*2];
int nxt[maxn*2],size[maxn],top[maxn],fa[maxn],son[maxn],dfn[maxn];
int ans;
int dep[maxn];
struct node{
    int id,val;
}a[maxn];
struct tree{
    int fa,l,r,len;
}f[maxn],ff;
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline void swap(int &x,int &y){x^=y^=x^=y;}
inline int abs(int x){return x<0?-x:x;}
inline int gcd(int x,int y){return y?x:gcd(y,x%y);}
inline int lcm(int x,int y){return x/gcd(x,y)*y;}
inline void clear(){memset(son,0,sizeof(son));memset(head,0,sizeof(head));for(int i=1;i<=n;i++)f[i]=ff; cnt=0,num=0,ans=0;}
inline void add(int x,int y,int z){to[++num]=y;nxt[num]=head[x]; val[num]=z; head[x]=num;}
inline bool cmp(node a,node b){return a.val>b.val;}
inline int getfa(int x)
{
    if(f[x].fa!=x)
    f[x].fa=getfa(f[x].fa);
    return f[x].fa;
}
void dfs1(int x,int ff)
{
    fa[x]=ff;
    size[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==ff) continue;
        fa[y]=x;
        dep[y]=dep[x]+1;
        dis[y]=dis[x]+val[i];
        dfs1(y,x);
        size[x]+=size[y];
        if(size[son[x]]<size[y])
        son[x]=y;
    }
    
}
void dfs2(int x,int ff)
{
    top[x]=ff;
    if(!son[x]) return ;
    dfs2(son[x],ff);
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        if(y==son[x]||y==fa[x]) continue;
        dfs2(y,y);
    }
    
}
inline int lca(int x, int y) 
{
    while(top[x]!=top[y]) 
    {
        if(dep[top[x]]<dep[top[y]]) 
        swap(x,y);
        x=fa[top[x]];
    }
    return dep[x]<dep[y]?x:y;
}
void merge(int x,int y,int val,int now)
{
    int maxx=0;
    x=getfa(f[x].fa),y=getfa(f[y].fa);
    f[y].fa=x;
    int xl=f[x].l,xr=f[x].r,yl=f[y].l,yr=f[y].r;
    int dis1=f[x].len,dis2=f[y].len;
    int dis3=dis[xl]+dis[yl]-2*dis[lca(xl,yl)];
    int dis4=dis[xl]+dis[yr]-2*dis[lca(xl,yr)];
    int dis5=dis[xr]+dis[yl]-2*dis[lca(xr,yl)];
    int dis6=dis[xr]+dis[yr]-2*dis[lca(xr,yr)];
    if(dis2>dis1)
    {
        f[x].l=f[y].l,f[x].r=f[y].r;
        f[x].len=dis2;
    }
    if(dis3>f[x].len)
    {
        f[x].l=xl,f[x].r=yl;
        f[x].len=dis3;
    }
    if(dis4>f[x].len)
    {
        f[x].l=xl,f[x].r=yr;
        f[x].len=dis4;
    }
    if(dis5>f[x].len)
    {
        f[x].l=xr,f[x].r=yl;
        f[x].len=dis5;
    }
    if(dis6>f[x].len)
    {
        f[x].l=xr,f[x].r=yr;
        f[x].len=dis6;
    }
    ans=max(ans,f[x].len*w[now]);
}
signed main()
{
    int t=read();
    while(t--)
    {
        clear();
        n=read();
        for(int i=1;i<=n;i++)
        {
            w[i]=read();
            a[i].val=w[i],a[i].id=i;
            f[i].fa=f[i].l=f[i].r=i;
        }
        int x,y,z;
        for(int i=1;i<=n-1;i++)
        {
            x=read(),y=read(),z=read();
            add(x,y,z);
            add(y,x,z);
        }
        dfs1(1,0);
        dfs2(1,1);
        sort(a+1,a+n+1,cmp);
        for(int i=1;i<=n;i++)
        {
            int x=a[i].id;
            for(int j=head[x];j;j=nxt[j])
            {
                int y=to[j];
                if(w[y]>=a[i].val)
                {
                    merge(x,y,val[j],x);
                }
            }
        }
        cout<<ans<<endl;
    }
}
T2

T3玫瑰花精

开始想打一棵平衡树,结果给打假了,后来发现其实跟之前考过的一道hotel差不多

用普通线段树维护一下l到r最左边的那个花精,最右边的,以及中间最大的连续无花精的区间,以及他们的中点p

然后就是一下基础的操作了,pushup稍微注意一下就好了

#include<bits/stdc++.h>
#define lid id<<1
#define rid id<<1|1
using namespace std;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
const int maxn=2000001;
int in[maxn],n,m;
struct tree{
    int ls,rs,mid,p,l,r;
}tr[maxn<<2];
void pushup(int id)
{
    if(tr[lid].ls) tr[id].ls=tr[lid].ls;
    else tr[id].ls=tr[rid].ls;
    if(tr[rid].rs) tr[id].rs=tr[rid].rs;
    else tr[id].rs=tr[lid].rs;
    if(!tr[rid].ls){tr[id].mid=tr[lid].mid;tr[id].p=tr[lid].p;return ;}
    if(!tr[lid].ls){tr[id].mid=tr[rid].mid;tr[id].p=tr[rid].p;return ;}
    int tmp=(tr[rid].ls-tr[lid].rs)>>1;
    if(tr[lid].mid>=tmp&&tr[lid].mid>=tr[rid].mid) tr[id].mid=tr[lid].mid,tr[id].p=tr[lid].p;
    else if(tmp>=tr[rid].mid) tr[id].mid=tmp,tr[id].p=(tr[rid].ls+tr[lid].rs)>>1;
    else tr[id].mid=tr[rid].mid,tr[id].p=tr[rid].p;
}
inline void  build(int id,int l,int r)
{
    tr[id].l=l; tr[id].r=r;
    if(l==r) return ;
    int mid=(l+r)>>1;
    build(lid,l,mid);
    build(rid,mid+1,r);
}
inline void update(int id,int pos,int val)
{
    if(tr[id].l==tr[id].r)
    {
        if(val==1)
        {
            tr[id].ls=tr[id].l;
            tr[id].rs=tr[id].r;
            tr[id].p=0;
            tr[id].mid=0;
            return ;
        }
        if(val==-1)
        {
            tr[id].p=0;
            tr[id].ls=0;
            tr[id].rs=0;
            tr[id].mid=0;
            return ;
        }
    }
    int mid=(tr[id].l+tr[id].r)>>1;
    if(pos<=mid) update(lid,pos,val);
    else update(rid,pos,val);
    pushup(id);
}
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("out","w",stdout);
    n=read();
    m=read();
    int pot,a;
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        pot=read(),a=read();
        if(pot==1)
        {
            if(tr[1].ls==0)
            {
                in[a]=1;
                printf("%d\n",in[a]);
                update(1,in[a],1);
                continue;
            }
            int minn=-0x7fffffff;
            if(tr[1].ls-1>minn)
            {
                minn=tr[1].ls-1;
                in[a]=1;
            }
            if(tr[1].mid>minn)
            {
                minn=tr[1].mid;
                in[a]=tr[1].p;
            }
            if(n-tr[1].rs>minn)
            {
                in[a]=n;
            }
            printf("%d\n",in[a]);
            update(1,in[a],1);
        }
        else
        {
            update(1,in[a],-1);
        }
    }
}
T3

 

posted on 2021-07-16 16:26  JYFHYX  阅读(37)  评论(0编辑  收藏  举报