100道codeforces2100分(施工中)

Problemset - Codeforces

 

考虑这些被释放的,值一定相同,并且等于区间gcd
于是用st表询问区间gcd,map套二分实现区间里某个数字出现次数

int n,a[100010];
int f[100010][20],lgn[100010];
map<int,vector<int>>pos;
int ask(int l,int r)
{
    int s=lgn[r-l+1];
    return __gcd(f[l][s],f[r-(1<<s)+1][s]);
}
int main() 
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++){
        f[i][0]=a[i]=read();
        pos[a[i]].push_back(i);
    }
    lgn[1]=0;
    for(int i=2;i<=n;i++)
        lgn[i]=lgn[i/2]+1;
    int logn=lgn[n];
    for(int j=1;j<=logn;j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            f[i][j]=__gcd(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    for(int m=read();m;m--)
    {
        int l=read(),r=read();
        int gcd=ask(l,r);
        if(lower_bound(pos[gcd].begin(),pos[gcd].end(),l)==pos[gcd].end())
            cout<<r-l+1<<"\n";
        else
            cout<<r-l+1-(upper_bound(pos[gcd].begin(),pos[gcd].end(),r)-lower_bound(pos[gcd].begin(),pos[gcd].end(),l))<<'\n';
    }
}
474F
注意到确定一个起点后,这棵树的答案就是固定的了,是Σd[i],其中d[x]表示到起点的路径上的点的数量
用换根dp维护之
具体地,第一遍dfs得到初始的d数组和f数组,f[i]表示令1为根,以i为根的子树的Σd[x]
第二遍dfs维护上面的点距离x的距离之和与上面的点的数量,往下走的时候修改一下即可

int n;
vector<int>e[200010];
ll f[200010],ans,d[200010],cnt[200010];
void dfs(int x)
{
    f[x]=d[x];
    cnt[x]=1;
    for(auto y:e[x]){
        if(d[y])
            continue;
        d[y]=d[x]+1;
        dfs(y);
        f[x]=f[x]+f[y];
        cnt[x]+=cnt[y];
    }
}
void dfs1(int x,ll sum,int num)
{
    ans=max(ans,f[x]-(d[x]-1)*cnt[x]+sum);
    for(auto y:e[x])
    {
        if(d[y]<d[x])
            continue;
        int numm=cnt[x]-cnt[y];//子树里除了y的子树还剩几个点
        dfs1(y,sum+num+f[x]-f[y]-(d[x]-2)*numm,num+numm);
    }
}
int main() 
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    d[1]=1;
    dfs(1);
    dfs1(1,0,0);
    cout<<ans;
}
1187E
考虑二分答案
对于mid,考虑枚举ai,则需要检查[k*a[i]+x,k*a[i]+a[i]-1]这个区间里有没有数字
复杂度nlog^2
不二分答案也行,考虑枚举ai,查找[k*a[i],(k+1)*a[i])区间里最后一个出现的数字,更新答案,复杂度仍然是nlog^2
int n,a[200010],c[1000010];
int check(int x)
{
    for(int i=1;i<=n;i++)
        for(int r=min(1000000,a[i]*2-1),l=a[i]+x;l<=r;l+=a[i],r=min(1000000,r+a[i]))
            if(c[r]-c[l-1])
                return 1;
    return 0;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    sort(a+1,a+1+n);
    n=unique(a+1,a+1+n)-a-1;
    for(int i=1;i<=n;i++)
        c[a[i]]=1;
    for(int i=1;i<=1000000;i++)
        c[i]+=c[i-1];
    int l=1,r=1000000,mid;
    while(l+1<r)
    {
        mid=(l+r)/2;
        if(check(mid))
            l=mid;
        else
            r=mid;
    }
    if(check(r))
        cout<<r;
    else if(check(l))
        cout<<l;
    else
        cout<<0;
}
484B
点分治板子
考虑1 2 3 4 5 6 7这个链应该怎么做
可以发现是4最高,26次之,1357最低
于是点分治的过程中给分治的那个点赋值即可

int vis[100010],siz[100010],wt[100010];
int n,Root,Tsiz,now='A'-1;
char ans[100010];
vector<int>e[100010];
void getroot(int x,int f)
{
    siz[x]=1;
    wt[x]=0;
    for(auto y:e[x])
    {
        if(y==f||vis[y])
            continue;
        getroot(y,x);
        siz[x]+=siz[y];
        wt[x]=max(wt[x],siz[y]);
    }
    wt[x]=max(wt[x],Tsiz-siz[x]);
    if(wt[Root]>wt[x])
        Root=x;
}
void DFS(int x)
{
    now++;
    ans[x]=now;
    vis[x]=1;
    for(auto y:e[x])
    {
        if(vis[y])
            continue;
        Root=0;
        Tsiz=siz[y];
        getroot(y,0);
        DFS(Root);
    }
    now--;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    wt[Root=0]=0x3f3f3f3f;
    Tsiz=n;
    getroot(1,0);
    ans[Root]=now;
    DFS(Root);
    for(int i=1;i<=n;i++)
        cout<<ans[i]<<' ';
}
321C

 

先跑一个最小生成树
对于边xyw
如果原本就被取用了,当然输出最小生成树的权值和
否则,考虑在最小生成树上跑lca来求xy路径上的最大边权,用w来替代之,输出新的边权和
int n,m,fa[200010],f[200010][20],maxx[200010][20],d[200010];
ll sum;
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);
}
struct edge
{
    int i,x,y,w,flag;
}o[200010];
bool w_(edge a,edge b)
{
    return a.w<b.w;
}
bool i_(edge a,edge b)
{
    return a.i<b.i;
}
vector<pair<int,int>>e[200010];
void dfs(int x)
{
    for(int i=0;i<e[x].size();i++)
    {
        int y=e[x][i].first,w=e[x][i].second;
        if(d[y])
            continue;
        d[y]=d[x]+1;
        f[y][0]=x;
        maxx[y][0]=w;
        dfs(y);
    }
}
int lca(int x,int y)
{
    int t=0;
    if(d[x]<d[y])
        swap(x,y);
    for(int i=19;i>=0;i--)
        if(d[f[x][i]]>=d[y])
        {
            t=max(t,maxx[x][i]);
            x=f[x][i];
        }
    if(x==y)
        return t;
    for(int i=19;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])
        {
            t=max(t,maxx[x][i]);
            t=max(t,maxx[y][i]);
            x=f[x][i];
            y=f[y][i];
        }
    }
    return max(max(maxx[x][0],maxx[y][0]),t);
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=1;i<=m;i++)
    {
        o[i].i=i;
        o[i].x=read();
        o[i].y=read();
        o[i].w=read();
    }
    sort(o+1,o+1+m,w_);
    for(int i=1;i<=m;i++)
    {
        if(get(o[i].x)==get(o[i].y))
            continue;
        e[o[i].x].push_back({o[i].y,o[i].w});
        e[o[i].y].push_back({o[i].x,o[i].w});
        sum=sum+o[i].w;
        o[i].flag=1;
        fa[fa[o[i].x]]=fa[o[i].y];
    }
    sort(o+1,o+1+m,i_);
    d[1]=1;
    dfs(1);
    for(int i=1;i<20;i++)
        for(int x=1;x<=n;x++)
        {
            f[x][i]=f[f[x][i-1]][i-1];
            maxx[x][i]=max(maxx[x][i-1],maxx[f[x][i-1]][i-1]);
        }
    for(int i=1;i<=m;i++)
        if(o[i].flag)
            printf("%lld\n",sum);
        else
            printf("%lld\n",sum-lca(o[i].x,o[i].y)+o[i].w);

}
609E

 

考虑二分答案x
把如果ai大于等于x,看做1,如果小于x,看做0,赋值给bi
于是问题转变成有没有长大于等于k的区间,满足区间和大于0
于是前缀和,问题转变成对于前缀和bi,在它k个之后,有没有大于他的
int n,k,a[200010],b[200010];
multiset<int>o;
int check(int x)
{
    o.clear();
    for(int i=1;i<=n;i++)
    {
        if(a[i]<x)
            b[i]=b[i-1]-1;
        else
            b[i]=b[i-1]+1;
        if(i>=k)
            o.insert(b[i]);
    }
    for(int l=0,r=k;r<=n;l++,r++)
    {
        if(*o.rbegin()>b[l])
            return 1;
        o.erase(o.find(b[r]));
    }
    return 0;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();k=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    int l=1,r=200000,mid;
    while(l+1<r)
    {
        mid=(l+r)/2;
        if(check(mid))
            l=mid;
        else
            r=mid;
    }
    if(check(r))
        cout<<r;
    else
        cout<<l;
}
1486D

 

着急吃饭导致wa了两发
实现一个倍增求lca,和一个在树上往上跳若干次,返回跳到哪里的函数
于是分类讨论
如果xy距离是奇数输出0
如果x=y输出n
如果d[x]=d[y],则把x跳到lca下面,y跳到lca下面,输出n-cnt[x]-cnt[y]
否则,把z跳到dis/2位置,x跳到z下面,输出cnt[z]-cnt[x]
int n,d[100010],f[100010][20],cnt[100010];
vector<int>e[100010];
int lca(int x,int y)
{
    if(d[x]<d[y])
        swap(x,y);
    for(int i=19;i>=0;i--)
        if(d[f[x][i]]>=d[y])
            x=f[x][i];
    if(x==y)
        return x;
    for(int i=19;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            x=f[x][i];
            y=f[y][i];
        }
    return f[x][0];
}
void dfs(int x)
{
    cnt[x]=1;
    for(auto y:e[x])
    {
        if(d[y])
            continue;
        d[y]=d[x]+1;
        f[y][0]=x;
        dfs(y);
        cnt[x]+=cnt[y];
    }
}
int ask(int x,int d)
{
    for(int i=19;i>=0;i--)
    {
        if(d>=(1<<i)){
            x=f[x][i];
            d-=(1<<i);
        }
    }
    return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    d[1]=1;
    dfs(1);
    for(int i=1;i<20;i++)
        for(int x=1;x<=n;x++)
            f[x][i]=f[f[x][i-1]][i-1];
    for(int m=read();m;m--)
    {
        int x=read(),y=read();
        if(x==y)
        {
            cout<<n<<'\n';
            continue;
        }
        if(d[x]<d[y])
            swap(x,y);
        int z=lca(x,y),dis=d[x]+d[y]-2*d[z];
        if(dis&1)
            cout<<"0\n";
        else if(d[x]==d[y])
        {
            z=ask(x,dis/2);
            x=ask(x,dis/2-1);
            y=ask(y,dis/2-1);
            cout<<n-cnt[x]-cnt[y]<<'\n';
        }
        else
        {
            z=ask(x,dis/2);
            x=ask(x,dis/2-1);
            cout<<cnt[z]-cnt[x]<<'\n';
        }
    }
}
519E

 

首先注意到,x的兄弟们一定和他深度相同
那这个p什么用处呢,他规定了询问都有p次祖先的兄弟们
于是跑一跑dfs序,预处理倍增,令y是x的p次祖先
被询问的兄弟就是y的子树里,和x同一深度的点的数量。也就是dfs序处于[l[y],r[y]]之间的深度是d[x]的点的数量
用vector维护之
int tot,l[100010],r[100010],f[100010][20],dfn[100010],d[100010];
vector<int>o[100010],e[100010];
void dfs(int x)
{
    tot++;
    l[x]=r[x]=dfn[x]=tot;
    o[d[x]].push_back(dfn[x]);
    for(auto y:e[x])
    {
        d[y]=d[x]+1;
        f[y][0]=x;
        dfs(y);
        r[x]=r[y];
    }
}
int ask(int x,int t)
{
    for(int i=19;i>=0;i--)
        if(t>=(1<<i)){
            t-=(1<<i);
            x=f[x][i];
        }
    return x;
}
int main()
{
    // freopen("1.in","r",stdin);
    int n=read();
    for(int i=1;i<=n;i++)
        e[read()].push_back(i);
    for(auto y:e[0])
    {
        d[y]=1;
        dfs(y);
    }
    for(int i=1;i<=19;i++)
        for(int x=1;x<=n;x++)
            f[x][i]=f[f[x][i-1]][i-1];

    for(int m=read();m;m--)
    {
        int x=read(),p=read();
        int y=ask(x,p);
        if(y==0)
            cout<<0<<' ';
        else
            cout<<upper_bound(o[d[x]].begin(),o[d[x]].end(),r[y])-lower_bound(o[d[x]].begin(),o[d[x]].end(),l[y])-1<<' ';
    }
}
208E

 

是不是走错地方了,2100显然比2000简单的多
考虑dp,对于每一行,01背包,f[i][j]表示选了i个数字,数字之和%k=j的最大数字之和
每行的求出来之后,再更新g[i][j]表示前i行数字之和%k=j的最大数字之和,这里是分组背包
n,m只有70,大力n^4做即可
int n,m,k,a[71],f[71][71],g[71][71];
void work()
{
    memset(f,0xef,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=m;i++)
    {
        a[i]=read();
        for(int cnt=i-1;cnt>=0;cnt--)
            for(int j=0;j<k;j++)
                f[cnt+1][(j+a[i])%k]=max(f[cnt+1][(j+a[i])%k],f[cnt][j]+a[i]);
    }
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();k=read();
    memset(g,0xef,sizeof(g));
    g[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int x=0;x<k;x++)
            g[i][x]=max(g[i][x],g[i-1][x]);
        work();
        for(int cnt=1;cnt<=m/2;cnt++)
            for(int x=0;x<k;x++)
            {
                g[i][x]=max(g[i][x],g[i-1][x]);
                for(int y=0;y<k;y++)
                    g[i][(x+y)%k]=max(g[i][(x+y)%k],f[cnt][y]+g[i-1][x]);
            }
    }
    cout<<g[n][0];
}
1433F

 

考虑从每个点bfs,每次从set里面挑点,如果和x有连边就是同一并查集,否则continue
复杂度只有(m+n)log

int n,m,vis[200010];
set<int>e[200010],o;
queue<int>q;
priority_queue<int,vector<int>,greater<int>>ans;
void bfs(int x)
{
    q.push(x);
    o.erase(x);
    int cnt=0;
    while(q.size())
    {
        x=q.front();
        q.pop();
        if(vis[x])continue;
        vis[x]=1;
        cnt++;
        for(auto i=o.begin();i!=o.end();)
        {
            int y=*i;
            i++;
            if(e[x].find(y)==e[x].end())
            {
                q.push(y);
                o.erase(y);
            }
        }
    }
    ans.push(cnt);
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        o.insert(i);
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        e[x].insert(y);
        e[y].insert(x);
    }
    for(int i=1;i<=n;i++)
    {
        if(vis[i])
            continue;
        bfs(i);
    }
    cout<<ans.size()<<'\n';
    while(ans.size())
    {
        cout<<ans.top()<<' ';
        ans.pop();
    }
}
920E

 

考虑区间dp
f[l][r]表示区间lr最少变成几个数字
f[l][r]=min(f[l][m]+f[m+1][r])
如果l,m和m+1,r都能变成一个数字,我们用v数组记录他变成了哪个数字,如果v也相同,flr就可以=1
int f[510][510];
int n,a[510],v[510][510];
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    memset(f,0x3f,sizeof(f));
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        v[i][i]=a[i];
        f[i][i]=1;
    }
    for(int len=2;len<=n;len++)
        for(int l=1,r=len;r<=n;l++,r++)
            for(int m=l;m<r;m++)
                if(f[l][m]==1&&f[m+1][r]==1&&v[l][m]==v[m+1][r])
                {
                    f[l][r]=1,v[l][r]=v[l][m]+1;
                    break;
                }
                else
                    f[l][r]=min(f[l][r],f[l][m]+f[m+1][r]);
    cout<<f[1][n];
}
1312E

 

如果已知gcd(a,b)和lcm(a,b),令t表示lcm(a,b)/gcd(a,b)的质因子种类数
则a和b的不同方案数是2的t次方
考虑问的是c*lcm-d*gcd=x,不妨令xcd除以gcd(x,c,d)
然后令a=agcd,b=bgcd,则lcm=abgcd
cab*gcd=d*gcd=x
这要求x整除gcd,可以枚举,复杂度t根号x
cab=x/gcd+d,这要求x/gcd+d整除c,可以模一下判断一下
ab=(x/gcd+d)/c
问题转变成互质的ab,已知乘积
只需要求出乘积的质因子种类数,求2的他的幂次就好了
那么问题转变成o1询问某个数字的质因子种类数
考虑用埃氏筛预处理一下,跑0.5s即可
int f[20000010];
ll ans,g[110];
ll c,d,x;
void add(ll gcd)
{
    ll t=x/gcd+d;
    if(t%c)
        return ;
    t=t/c;
    ans=ans+g[f[t]];
}
void work()
{
    ans=0;
    c=read(),d=read(),x=read();
    ll t=__gcd(__gcd(c,d),x);
    c=c/t;d=d/t;x=x/t;
    for(int gcd=1;gcd*gcd<=x;gcd++)
    {
        if(x%gcd)
            continue;
        add(gcd);
        if(x/gcd!=gcd)
            add(x/gcd);
    }
    cout<<ans<<'\n';
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int i=2;i<=20000000;i++)
    {
        if(f[i])continue;
        for(int j=i;j<=20000000;j+=i)
            f[j]++;
    }
    g[0]=1;
    for(int i=1;i<=100;i++)
        g[i]=g[i-1]*2;
    for(int t=read();t;t--)
        work();
}
1499D

 

手玩一下n=5和n=7,可以发现一定是选2个相邻的,剩下的隔一个选一个
于是枚举相邻的是哪个,隔一个选一个可以用前缀和o1求出来
int n,a[200010];
ll ans,c[200010];
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read();
    c[1]=a[1];
    for(int i=2;i<=n;i++)
        c[i]=c[i-2]+a[i];
    ans=c[n];
    for(int i=1;i<n;i++)
        ans=max(ans,c[i]+c[n-i%2]-c[i-1]);
    cout<<ans;
}
1372D

 

构造题
考虑斜着第一列,第四列,第七列。。。
和第二列,第五列,第八列。。。
和第三列,第六列,第九列。。。
在这三列里选,取min,总有至少一个小于等于sum/3
于是看看谁最小,用它即可
char s[1010][1010];
int n,dx[]={0,0,1,-1},dy[]={1,-1,0,0},cnt[10];
int ask(int x,int y)
{
    if(x>n||y>n)
        return 0;
    for(int i=0;i<4;i++)
    {
        if(min(x+2*dx[i],y+2*dy[i])<1||max(x+2*dx[i],y+2*dy[i])>n)
            continue;
        if(s[x][y]=='X'&&s[x+dx[i]][y+dy[i]]=='X'&&s[x+2*dx[i]][y+2*dy[i]]=='X')
            return 1;
    }
    for(int i=0;i<4;i++)
        if(s[x][y]=='X'&&s[x+dx[i]][y+dy[i]]=='X'&&s[x-dx[i]][y-dy[i]]=='X')
            return 1;
    return 0;
}
void work()
{
    n=read();
    for(int i=1;i<=n;i++)
        scanf("%s",s[i]+1);
    for(int j=1;j<=3;j++)
    {  
        cnt[j]=0;
        for(int i=j;i<=3*n;i+=3)
            for(int x=1,y=i;y;x++,y--)
                if(ask(x,y))
                    cnt[j]++;
    }
    int j;
    if(min(cnt[1],min(cnt[2],cnt[3]))==cnt[1])
        j=1;
    else if(min(cnt[1],min(cnt[2],cnt[3]))==cnt[2])
        j=2;
    else
        j=3;

    for(int i=j;i<=3*n;i+=3)
        for(int x=1,y=i;y;x++,y--)
            if(ask(x,y))
                s[x][y]='O';
    for(int i=1;i<=n;i++)
        printf("%s\n",s[i]+1);
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int t=read();t;t--)
        work();
}
1450C1

 

数学,计算几何题
最多100边形,考虑枚举一下,判断夹角是否能够整除360/n

struct node
{
    double x,y;
}A,B,C,D;
double v1,v2,v3,r,v[5];
double ask(node a,node b,node c)
{
    a.x-=c.x;
    a.y-=c.y;
    b.x-=c.x;
    b.y-=c.y;
    return acos((a.x*b.x+a.y*b.y)/r/r);
}
void work(int n)
{
    double t=2*acos(-1)/n;

    for(int i=1;i<=3;i++)
        if( fabs(v[i]-round(v[i]/t)*t)>1e-5)
            return ;
    printf("%8lf",r*r*sin(t)*n/2);
    exit(0);
}
int main()
{
    // freopen("1.in","r",stdin);
    cin>>A.x>>A.y>>B.x>>B.y>>C.x>>C.y;
    D.x=((B.y-A.y)*(C.y*C.y-A.y*A.y+C.x*C.x-A.x*A.x)-(C.y-A.y)*(B.y*B.y-A.y*A.y+B.x*B.x-A.x*A.x))/(2.0*((C.x-A.x)*(B.y-A.y)-(B.x-A.x)*(C.y-A.y)));
    D.y=((B.x-A.x)*(C.x*C.x-A.x*A.x+C.y*C.y-A.y*A.y)-(C.x-A.x)*(B.x*B.x-A.x*A.x+B.y*B.y-A.y*A.y))/(2.0*((C.y-A.y)*(B.x-A.x)-(B.y-A.y)*(C.x-A.x)));
    r=(A.x-D.x)*(A.x-D.x)+(A.y-D.y)*(A.y-D.y);
    r=sqrt(r);
    v[1]=ask(A,B,D);
    v[2]=ask(A,C,D);
    v[3]=ask(B,C,D);
    for(int n=3;n<=100;n++)
        work(n);
}
1C
树上dfs序相关
给v注水可以使用dfs序,线段树区间修改,再把区间里的set里的点删掉,替换为v的父亲
清空节点v,我们可以使用set插入
判断v是否装满水,我们先看看set里面有没有v的儿子,如果有,显然输出0。否则输出线段树单点询问的值即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read()
{
    int x;scanf("%d",&x);return x;
}
int cnt,dfn[500010],l[500010],r[500010];
int n,lazy[2000010],fa[500010];
vector<int>e[500010];
set<int>o;
void dfs(int x)
{
    cnt++;
    dfn[x]=l[x]=r[x]=cnt;
    for(auto y:e[x])
    {
        if(dfn[y])
            continue;
        fa[y]=x;
        dfs(y);
        r[x]=r[y];
    }
}
void build(int x,int l,int r)
{
    lazy[x]=-1;//全是空的
    if(l==r)
        return ;
    int mid=(l+r)/2;
    build(x*2,l,mid);
    build(x*2+1,mid+1,r);
}
void add(int x,int l,int r,int tl,int tr)
{
    if(lazy[x]==1)
        return;
    if(tl<=l&&r<=tr)
    {
        lazy[x]=1;
        return ;
    }
    int mid=(l+r)/2;
    if(tl<=mid)
        add(x*2,l,mid,tl,tr);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr);
    if(lazy[x*2]==1&&lazy[x*2+1]==1)
        lazy[x]=1;
    else
        lazy[x]=-1;
}
int ask(int x,int l,int r,int d)
{
    if(lazy[x]==1)
        return 1;
    if(l==r)
        return 0;
    int mid=(l+r)/2;
    if(d<=mid)
        return ask(x*2,l,mid,d);
    else
        return ask(x*2+1,mid+1,r,d);
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1);
    build(1,1,n);
    for(int q=read();q;q--)
    {
        int t=read();
        if(t==1)
        {
            int x=read();
            add(1,1,n,l[x],r[x]);
            while(1)
            {
                auto t=o.lower_bound(l[x]);
                if(t==o.end()||*t>r[x])
                    break;
                o.erase(t);
                if(x==1)
                    continue;
                o.insert(dfn[fa[x]]);
            }
        }
        else if(t==2)
            o.insert(dfn[read()]);
        else 
        {
            int x=read();
            auto t=o.lower_bound(l[x]);
            if(t!=o.end()&&*t<=r[x])
            {
                cout<<"0\n";
                continue;
            }
            cout<<ask(1,1,n,dfn[x])<<'\n';
        }
    }
}
343D
使用直径和并查集的知识解决本题,在并查集合并的途中更新直径。
因为两个联通块相连,为了使得新连通块直径最长,需要连接两个直径的中点,分类讨论一下,发现新的直径为t=max((len[x]+1)/2+(len[y]+1)/2+1,max(len[x],len[y]))

int fa[300010],t,maxx,len[300010];
int n,m,q;
vector<int>o,e[300010];
int get(int x)
{
    return fa[x]==x?x:fa[x]=get(fa[x]);
}
void dfs(int x,int beg,int d)
{
    if(d>maxx){
        t=x;
        maxx=d;
    }
    o.push_back(x);
    fa[x]=beg;
    for(auto y:e[x])
    {
        if(fa[y])
            continue;
        dfs(y,beg,d+1);
    }
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();q=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for(int i=1;i<=n;i++)
    {
        if(fa[i])
            continue;
        maxx=0;
        t=i;;
        o.clear();
        dfs(i,i,0);
        for(auto x:o)
            fa[x]=0;
        o.clear();
        dfs(t,i,0);
        len[i]=maxx;
    }
    for(;q;q--)
    {
        if(read()==1)
            cout<<len[get(read())]<<'\n';
        else
        {
            int x=get(read());
            int y=get(read());
            if(x==y)
                continue;
            int t=max((len[x]+1)/2+(len[y]+1)/2+1,max(len[x],len[y]));
            fa[x]=y;
            len[y]=t;
        }
    }
}
455C
树上多源bfs
既然原图是合法的,把每个点的管辖范围的交界处的边删掉即可。
int n,k,d,v[300010];
queue<int>q;
map<int,int>o[300010];
vector<int>e[300010],ans;
int main()
{
    // freopen("1.in","r",stdin);

    n=read();k=read();d=read();
    for(int i=1;i<=k;i++)
    {
        int x=read();
        q.push(x);
        v[x]=x;
    }
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        o[x][y]=i;
        e[x].push_back(y);
        e[y].push_back(x);
    }
    while(q.size())
    {
        int x=q.front();q.pop();
        for(auto y:e[x])
        {
            if(v[y])
                continue;
            v[y]=v[x];
            q.push(y);
        }
    }
    for(int x=1;x<=n;x++)
        for(auto y:e[x])
            if(v[x]!=v[y]&&o[x][y])
                ans.push_back(o[x][y]);
    cout<<ans.size()<<'\n';
    for(auto v:ans)
        cout<<v<<' ';
}
796D

 

跑一个dfs序,用一个long long来存储一个区间里每个颜色在区间里是否出现过,则区间修改使用线段树修改一下,区间询问用线段树区间询问一下。

ll a[1600010],lazy[1600010],c[400010];
int l[400010],r[400010],cnt;
int n,m;
vector<int>e[400010];
void pushdown(int x)
{
    lazy[x*2]=lazy[x*2+1]=lazy[x];
    a[x*2]=a[x*2+1]=lazy[x];
    lazy[x]=0;
}
int count(ll x)
{
    int sum=0;
    while(x)
    {
        sum++;
        x=x-(x&(-x));
    }
    return sum;
}
void add(int x,int l,int r,int d,ll v)
{
    a[x]|=v;
    if(l==r)
        return ;
    int mid=(l+r)/2;
    if(d<=mid)
        add(x*2,l,mid,d,v);
    else
        add(x*2+1,mid+1,r,d,v);
}
void dfs(int x)
{
    l[x]=r[x]=++cnt;
    add(1,1,n,cnt,c[x]);
    for(auto y:e[x])
    {
        if(l[y])
            continue;
        dfs(y);
    }
    r[x]=cnt;
}
void add(int x,int l,int r,int tl,int tr,ll v)
{
    if(tl<=l&&r<=tr)
    {
        lazy[x]=a[x]=v;
        return ;
    }
    if(lazy[x])
        pushdown(x);
    int mid=(l+r)/2;
    if(tl<=mid)
        add(x*2,l,mid,tl,tr,v);
    if(tr>mid)
        add(x*2+1,mid+1,r,tl,tr,v);
    a[x]=a[x*2]|a[x*2+1];
}
ll ask(int x,int l,int r,int tl,int tr)
{
    if(tl<=l&&r<=tr)
        return a[x];
    if(lazy[x])
        pushdown(x);
    int mid=(l+r)/2;
    ll t=0;
    if(tl<=mid)
        t=ask(x*2,l,mid,tl,tr);
    if(tr>mid)
        t|=ask(x*2+1,mid+1,r,tl,tr);
    return t;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=n;i++)
        c[i]=(1ll<<(read()));
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    dfs(1);
    for(;m;m--)
    {
        if(read()&1)
        {
            int x=read();
            add(1,1,n,l[x],r[x],1ll<<read());
        }
        else
        {
            int x=read();
            cout<<count(ask(1,1,n,l[x],r[x]))<<'\n';
        }
    }
}
620E

 

有向图连通性,写一个tarjan类似物。
如果没有环,显然是一种颜色。否则把回边赋值为2号颜色,出边赋值为1号颜色
int col[5010],n,m,cy,ans[5010];
vector<pair<int,int>>e[5010];
void dfs(int x)
{
    col[x]=1;
    for(auto it:e[x])
    {
        int y=it.first,i=it.second;
        if(!col[y])
        {
            dfs(y);
            ans[i]=1;
        }
        else if(col[y]==2)
            ans[i]=1;
        else
        {
            ans[i]=2;
            cy=1;
        }
    }
    col[x]=2;
}
int main()
{
    // freopen("1.in","r",stdin);
    n=read();m=read();
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        e[x].push_back({y,i});
    }
    for(int i=1;i<=n;i++)
        if(col[i]==0)
            dfs(i);
    cout<<cy+1<<'\n';
    for(int i=1;i<=m;i++)
        cout<<ans[i]<<' ';
}
1217D

 

能随意修改最短路的长度,考虑先求bfs最短路,然后枚举a到b和b到c的交叉点x,先走a到x,再走x到b,再走b到x,再走x到c。则b到x的部分会走两遍,贪心地让这段最短,其次让a到x的部分和x到c的部分最短。对所有的x取min即可

int n,m,a,b,c,d[200010][4],vis[200010][4],now;
vector<int>e[200010];
ll p[200010],ans;
queue<int>q;
void bfs(int x)
{
    vis[x][now]=1;
    q.push(x);
    while(q.size())
    {
        x=q.front();
        q.pop();
        for(auto y:e[x])
        {
            if(vis[y][now])
                continue;
            d[y][now]=d[x][now]+1;
            vis[y][now]=1;
            q.push(y);
        }
    }
}
void work()
{
    n=read();m=read();a=read();b=read();c=read();
    for(int i=1;i<=n;i++)
        e[i].clear();
    for(int i=1;i<=m;i++)
        p[i]=read();
    sort(p+1,p+1+m);
    for(int i=1;i<=m;i++)
        p[i]=p[i-1]+p[i];
    for(int i=1;i<=m;i++)
    {
        int x=read(),y=read();
        e[x].push_back(y);
        e[y].push_back(x);
    }
    for(int i=1;i<=n;i++)
        vis[i][1]=vis[i][2]=vis[i][3]=0;
    now=1;d[a][1]=0;
    bfs(a);
    now=2;d[b][2]=0;
    bfs(b);
    now=3;d[c][3]=0;
    bfs(c);
    ll ans=1e18;
    for(int x=1;x<=n;x++)
    {
        if(d[x][1]+d[x][2]+d[x][3]>m)
            continue;
        ans=min(ans,p[d[x][1]+d[x][2]+d[x][3]]+p[d[x][2]]);
    }
    cout<<ans<<'\n';
}
int main()
{
    // freopen("1.in","r",stdin);
    for(int t=read();t;t--)
        work();
}
1343E
数学,stl
预处理一个vi表示i的最大质因子,则对修改的数字x进行质因子分解,复杂度是log的
考虑每次单点修改,对着位置i狠狠修改,顺便更新全局gcd即可。具体地,如果某个质因子的次数够了n次,则gcd乘上这个质因子,复杂度qlog^2

map<int,int>o[200010];
map<int,int>cnt[200010];
int n,q,a[200010],v[200010];
ll ans=1,mod=1e9+7;
int main()
{
    // freopen("1.in","r",stdin);
    for(int i=2;i<=200000;i++)
    {
        if(v[i])continue;
        for(int j=i;j<=200000;j+=i)
            v[j]=i;
    }
    n=read();q=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        while(a[i]!=1)
        {

            o[i][v[a[i]]]++;
            cnt[v[a[i]]][o[i][v[a[i]]]]++;
            if(cnt[v[a[i]]][o[i][v[a[i]]]]==n)
                ans=ans*v[a[i]]%mod;
            a[i]=a[i]/v[a[i]];
        }
    }
    for(;q;q--)
    {
        int i=read();
        a[i]=read();
        while(a[i]!=1)
        {

            o[i][v[a[i]]]++;
            cnt[v[a[i]]][o[i][v[a[i]]]]++;
            if(cnt[v[a[i]]][o[i][v[a[i]]]]==n)
                ans=ans*v[a[i]]%mod;
            a[i]=a[i]/v[a[i]];
        }
        cout<<ans<<'\n';
    }
}
1493D

 

注意到n只有200,大胆dp
用f[cnt][j]表示有cnt个数字,j个5,最多能拿到几个2。
输出max(min(i,f[k][i]))

int n,k,f[210][4000],sum,ans;
int main()
{
    // freopen("1.in","r",stdin);
    n=read();k=read();
    memset(f,0xef,sizeof(f));
    f[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        ll a=read();
        int n2=0,n5=0;
        while(a%2==0)
            n2++,a/=2;
        while(a%5==0)
            n5++,a/=5;
        sum+=n5;
        for(int cnt=k;cnt>=1;cnt--)
            for(int j=sum;j>=n5;j--)
                f[cnt][j]=max(f[cnt][j],f[cnt-1][j-n5]+n2);
    }
    for(int i=0;i<=sum;i++)
        ans=max(ans,min(i,f[k][i]));
    cout<<ans;
}
837D

 

把描述st不相等的数组算一算
        if(s[i]==t[i])
            a[i]=a[i-1];
        else if(s[i]=='1')
            a[i]=a[i-1]+1;
        else
            a[i]=a[i-1]-1;
则答案为a数组的max(a[l]-a[r]),维护一下前缀最大最小值并更新答案即可。
int n,a[1000010];
char s[1000010],t[1000010];
int main()
{
    // freopen("1.in","r",stdin);
    n=read();
    scanf("%s",s+1);
    scanf("%s",t+1);
    for(int i=1;i<=n;i++)
    {
        if(s[i]==t[i])
            a[i]=a[i-1];
        else if(s[i]=='1')
            a[i]=a[i-1]+1;
        else
            a[i]=a[i-1]-1;
    }
    if(a[n])
        cout<<"-1";
    else
    {
        int ans=0,minn=0,maxx=0;
        for(int i=1;i<=n;i++)
        {
            minn=min(minn,a[i]);
            maxx=max(maxx,a[i]);
            ans=max(ans,a[i]-minn);
            ans=max(ans,maxx-a[i]);
        }
        cout<<ans;
    }
}
1370E

 

posted @ 2024-01-01 17:27  zzuqy  阅读(57)  评论(0编辑  收藏  举报