总之就是 | ZROI NOIP21冲刺 Day4

「启」

今天 A 因为自己想当然加了个覆盖特殊取值答案的操作挂掉了五十分(

今天就可以说是 DP + Bitset 场了,这对我非常不友好(

下面的 Code 缺省源均为 「V5」.

「A」史上最简洁的题面

这个题吃尽罚时但是还是挂了 50 pts.

因为原题面就很简洁了所以我也不需要简化了:D

「A」题意简述

给定一个 \(n\) 个点 \(m\) 条边的无向图,对于每一个在 \([0,m]\) 内的 \(i\),求有多少中黑白染色方案使得有 \(i\) 条边两端的点都是黑色的。

数据范围:\(n \le 25, 1 \le m \le \frac{n(n-1)}{2}.\)

时间限制:本来是 1s,后来放宽到 2s.

「A」思路简述

因为这个 \(n\) 很小,所以我们考虑用二进制数来表示所有点的状态,第 \(i\) 位是 \(1\) 表明第 \(i\) 个点被染成了黑色。

那么显然一共就有 \(2^n\) 种染色方案,下面来考虑转移状态。

显然两个只相差一个点颜色不同的状态可以互相转移,为了统一,这里转移的方向是从黑色少的状态向黑色多的状态转移,也就是二进制数从小到大转移。

为了方便,我们统一从 x-lowbit(x) 转移到 x,记 x 和其他点的连通性情况的二进制数为 cnt.

那么每次新增的两端点都为黑色的边的个数即为 popcount(x-lowbit(x)&cnt).

这样我们去递推转移即可,使用了 bitset 以及其函数 .count,复杂度为 \(O(\dfrac{n2^n}{w}).\)

「A」Code

CI LMT(4e7),MXX(1001);

int n,m,ans[MXX],f[LMT],pos[LMT];

bitset<25> cnt[MXX];

S main()
{
    Files();
    fr(n),fr(m);

    for(int i(0);i<n;++i) pos[1<<i]=i+1;

    for(int i(1);i<=m;++i)
    {
        int x,y;

        fr(x),fr(y);cnt[x][y]=1;cnt[y][x]=1;
    }

    for(int i(1);i<=n;++i) cnt[i]>>=1;

    ans[0]=1;

    for(int i(1);i<(1<<n);++i)
    {
        int now(i),p(pos[lowbit(i)]);
        bitset<25> temp(now);
        temp&=cnt[p];now-=lowbit(i);
        f[i]=f[now]+temp.count();++ans[f[i]];
    }

    for(int i(0);i<=m;++i) fw(ans[i],0);

    Heriko Deltana;
}

「B」史上第二简洁的题面

但其实 B 的题面比 A 简洁(x)

「B」题目简述

给定一个长度为 \(n\) 的序列 \(a\),给定 \(m\) 个询问 \(l,r,x\),求 \(a_{l},a_{l+1}\cdots,a_{r}\) 中有多少个数与 \(x\) 互质。

数据范围:\(n,m,a_i,x \le 10^5.\)

时间限制:1s.

「B」思路简述

最一开始以为是数据结构题,后来发现其实是数学题,还用到了莫反(

暴力做法很简单,随便乆能写,保证了我不会爆零(

用柿子来表示本题所求即为:

\[\sum\limits_{i=l}^r [(a_i,x)=1] \]

那么既然是数学题,那就煺柿子:

\[\begin{aligned} \sum\limits_{i=l}^r [(a_i,x)=1] &= \sum\limits_{i=l}^r\sum\limits_{d|a_i,d|x} \mu(d) \\ &= \sum\limits_{d|x} \mu(d)\sum\limits_{i=l}^r[d|a_i] \end{aligned} \]

我们可以在 \(O(\sqrt{n})\) 的时间之内得到每个数的因数,同时可以用线性筛预处理出值域范围内的 \(\mu.\)

为了便于求解,我们把问题离线下来,分成两段求解。

「B」Code


CI MXX(1e5+1),QXX(2e5+1);

int n,m,a[MXX],mu[MXX],prime[MXX],nopr[MXX],tot[MXX],cnt,ans[MXX];

I void Es()
{
    mu[1]=1;

    for(int i(2);i<=MXX;++i)
    {
        if(!nopr[i]) prime[++cnt]=i,mu[i]=-1;

        for(int j(1);i*prime[j]<=MXX and j<=cnt;++j)
        {
            nopr[i*prime[j]]=1;

            if(i%prime[j]) mu[i*prime[j]]=-mu[i];
            else break;
        }
    }
}

struct Query
{
    int opt,id,pos,x;

    I bool operator < (const Query &co) const {Heriko pos<co.pos;}
}

q[QXX];

vector<int> fac[MXX];

I void GetFactor(int x)
{
    if(fac[x].size()) Heriko;

    for(int i(1);i*i<=x;++i)
        if(!(x%i))
        {
            fac[x].push_back(i);

            if(i!=x/i) fac[x].push_back(x/i);
        }
}

S main()
{
    Files();
    
    Es();fr(n),fr(m);int qm(m<<1);

    for(int i(1);i<=n;++i) fr(a[i]);

    for(int i(1);i<=m;++i)
    {
        int l,r,x;fr(l),fr(r),fr(x);
        q[i*2-1]=(Query){-1,i,l-1,x};
        q[i*2]=(Query){1,i,r,x};
    }

    sort(q+1,q+1+qm);

    for(int i(1),j(1);i<=qm;++i)
    {
        for(;j<=n and j<=q[i].pos;++j)
        {
            GetFactor(a[j]);

            for(auto k:fac[a[j]]) ++tot[k];
        }

        GetFactor(q[i].x);

        for(auto k:fac[q[i].x]) ans[q[i].id]+=q[i].opt*mu[k]*tot[k];
    }
        
    for(int i(1);i<=m;++i) fw(ans[i],1);

    Heriko Deltana;
}

「C」史上第三简洁的题面

又是一个 DP + Bitset

「C」题目简述

\(n\) 个人排成一排,从左到右编号为 \(1...n\),接下来你每次可以指定两个相邻的人战斗,输的一方将会离开队伍,直到最后只有一个人留在队伍里,这个人将会称为最后的赢家。你知道任意两个人打谁会赢,求哪些人可能会成为最后的赢家。

数据范围:\(n \le 2000.\)

时间限制:2s.

「C」思路简述

最一开始以为是要建图,但是后来发现只能让相邻的人干架(

于是就考虑区间 DP,设 \(f(l,r,k)\) 表示 \(k\) 能否在区间 \([l,r]\) 取得胜利,转移的时候枚举 \(k\) 之后再分别从左右枚举转移是否能赢,这样做的复杂度为 \(O(n^4)\),能过 \(60\) pts.

这个时候我们考虑使用 bitset 进行优化,复杂度降至 \(O(\dfrac{n^4}{w})\),但是这样的常数优化显然无法让我们过掉这个题。

于是我们考虑对 DP 的过程复杂度进行进一步的优化,考虑到 \(i\) 的胜利条件只需要左右都让它赢即可,所以我们用空间换时间,把左右的过程分开数组,每次 DP 的时候再互相转移,再加上 bitset 优化,就能把复杂度降至 \(O(\dfrac{n^3}{w}).\)

这样乆能过掉这个题了。

「C」Code

把上文中对应两个 Code 的都放在了这里

「C」60pts

CI MXX(2001);

int n;

bitset<MXX> co[MXX],f[MXX][MXX];

I bool GET()
{
    char c(getchar());

    while(1)
        if(c=='1') Heriko Romanno;
        else if(c=='0') Heriko Deltana;
        else c=getchar();
}

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;++i)
        for(int j(1);j<=n;++j)
            co[i][j]=GET();

    for(int i(1);i<=n;++i) f[i][i][i]=1;

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

    for(int len(3);len<=n;++len)
    {
        for(int i(1),j(len);j<=n;++i,++j)
        {
            for(int k(i);k<=j;++k)
            {
                int l(0),r(0);

                if(k==i) l=1;
                else
                {
                    for(int w(i);w<k;++w)
                        if(co[k][w] and f[i][k-1][w])
                        {
                            l=1;break;
                        }
                }

                if(k==j) r=1;
                else 
                {
                    for(int w(j);w>k;--w)
                        if(co[k][w] and f[k+1][j][w])
                        {
                            r=1;break;
                        } 
                }

                if(l&r) f[i][j][k]=1;
            }
        }
    }

    for(int i(1);i<=n;++i)
        if(f[1][n][i])
            fw(i,0);

    Heriko Deltana;
}

「C」100pts

I bool GET()
{
    char c(getchar());

    while(1)
        if(c=='1') Heriko Romanno;
        else if(c=='0') Heriko Deltana;
        else c=getchar();
}

CI MXX(2001);

int n;

bitset<MXX> f[2][MXX],co[MXX],ans;

S main()
{
    Files();

    fr(n);

    for(int i(1);i<=n;f[0][i][i]=f[1][i][i]=1,++i)
        for(int j(1);j<=n;++j)
            co[i][j]=GET();

    for(int len(0);len<n;++len)
        for(int l(1);l+len<=n;++l)
        {
            int r(l+len);
            ans=f[0][l]&f[1][r];
            f[1][r][l-1]=(co[l-1]&ans).any();
            f[0][l][r+1]=(co[r+1]&ans).any();
        }

    for(int i(1);i<=n;++i)
        if(ans[i])
            fw(i,0);

    Heriko Deltana;
}

「D」史上第三简洁的题面

这题把我调炸了,写的函数除了 DijkstraCoSet 全都炸了。

虽然称作史上第三,但是这个题的题面已经需要我化简了(

「D」题目简述

给出一个 \(n\) 个点 \(m\) 条边的无向图,求对于给定的结点 \(v\) 求单源最短路。

但是有一条不能经过的边,这些边只有在到达其一端的时候才能被知道,求在最优的策略下,最坏情况下多长时间能到达 \(v\)

数据范围:\(n \le 10^6, m\le 2 \times 10^6.\)

时间限制:7s.

「D」思路简述

首先既然是单源最短路那我们乆写一手 Dijkstra.

接下来考虑怎么才能构造出最坏情况,那么不能走的边乆是从 \(v\) 到其他点的最短路上的一条边。

那我们求得断掉一条边只后的最优策略路径长度 \(val\) 之后,设答案为 \(f(x)\),则有:

\[f(x) = \max\{val(u),\min\limits_{(u,v) \in E}\{f(u),w(i,v)\}\} = \min\limits_{(u,v) \in E}\{\max\{val(v),f(u)+w(i,v)\}\}. \]

「D」Code

template<typename J>
I J Hmax(const J &x,const J &y) {Heriko x>y?x:y;}

CI MXX(4e6+1),NXX(1e6+1);const LL INF(1e17+114514191810);

struct Node
{
    int from,nex,to;LL val;bool tag;

    I bool operator < (const Node &co) const {Heriko val<co.val;}
}

r[MXX],r2[MXX];int cnt(1),cnt2,head[NXX];

I void Add(int x,int y,LL z)
{
    r[++cnt]=(Node){x,head[x],y,z,0};head[x]=cnt;
    r[++cnt]=(Node){y,head[y],x,z,0};head[y]=cnt;
}

priority_queue< pair<LL,int> > q;

int n,m,s;

LL dis[NXX],f[NXX],val[NXX];

bool vis[NXX];

I void Dijkstra()
{
    mst(vis,0);mst(dis,0x3f);dis[s]=0;q.push(pair<LL,int>(0,s));

    while(q.size())
    {
        int x(q.top().second);q.pop();

        if(vis[x]) continue;

        vis[x]=1;

        for(int i(head[x]);i;i=r[i].nex)
        {
            int y(r[i].to);

            if(dis[y]>dis[x]+r[i].val)
            {
                dis[y]=dis[x]+r[i].val;

                q.push(pair<LL,int>(-dis[y],y));
            }
        }
    }
}

int fa[NXX],dep[NXX];

void DFS(int x,int father)
{
    dep[x]=dep[father]+1;vis[x]=1;fa[x]=father;

    for(int i(head[x]);i;i=r[i].nex)
    {
        int y(r[i].to);

        if(y==father) continue;

        if(vis[y] or dis[y]!=dis[x]+r[i].val) continue;

        r[i].tag=1;DFS(y,x);
    }
}

int cofa[NXX];

I int Find(int x)
{
    while(x!=cofa[x]) x=cofa[x]=cofa[cofa[x]];

    Heriko x;
}

I void CoSet(int co)
{
    int x(r2[co].from),y(r2[co].to);LL v(r2[co].val);

    if(!dep[x] or !dep[y]) Heriko;

    x=Find(x),y=Find(y);

    while(x!=y)
    {
        if(dep[x]<dep[y]) swap(x,y);

        val[x]=v;cofa[x]=fa[x];x=Find(x);
    }
}

I void BFS()
{
    while(q.size()) q.pop();

    mst(vis,0);mst(f,0x3f);f[s]=0;q.push(pair<LL,int>(0,s));

    while(q.size())
    {
        int x(q.top().second);q.pop();

        if(vis[x]) continue;

        vis[x]=1;

        for(int i(head[x]);i;i=r[i].nex)
        {
            int y(r[i].to);

            if(vis[y]) continue;

            if(f[y]>Hmax(val[y],f[x]+r[i].val))
            {
                f[y]=Hmax(val[y],f[x]+r[i].val);
                q.push(pair<LL,int>(-f[y],y));
            }
        }
    }
}

I LL Calc(int x) {Heriko dis[r[x].from]+dis[r[x].to]+r[x].val;}

S main()
{
    Files();
    fr(n),fr(m),fr(s);
    
    for(int i(1);i<=n;++i) cofa[i]=i;

    for(int i(1);i<=m;++i)
    {
        int x,y;LL z;
        fr(x),fr(y),fr(z);
        Add(x,y,z);
    }

    Dijkstra();mst(vis,0);DFS(s,0);

    for(int i(2);i<cnt;i+=2)
    {
        if(r[i].tag or r[i^1].tag) continue;

        r2[++cnt2]=r[i];r2[cnt2].val=Calc(i);
    }

    sort(r2+1,r2+1+cnt2);mst(val,0x3f);

    for(int i(1);i<=cnt2;++i) CoSet(i);

    for(int i(1);i<=n;++i) val[i]-=dis[i];

    BFS();

    for(int i(1);i<=n;++i) fw(f[i]>INF/10?-1:f[i],0);
    
    Heriko Deltana;
}

「终」

虽然但是,没啥好说的。

posted @ 2021-10-16 10:05  HerikoDeltana  阅读(88)  评论(2编辑  收藏  举报