多校>=20部分题题解

版权声明:转载之前请说你是博主的粉丝哦。 https://blog.csdn.net/qq_34921856/article/details/82656040

多校的题终于补完了,留下了几道平衡树及数论反演的题到时候再补。。
A - RMQ Similar Sequence
笛卡尔树裸题,但笛卡尔树的建树过程感觉建出来的树很。。不平衡。。所以在dfs的时候容易爆栈。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+5;
const int mod=1e9+7;
int inv[maxn];
void invTable(int n, int p, int inv[]) {
    inv[1] = 1;
    for(int i = 2; i <= n; ++i)
        inv[i] = 1LL*(p-p/i) * inv[p%i] % p;
}
int n;
int stk[maxn],len;
struct node
{
    int l,r,p,v,id; //l r 左右孩子 p父亲 v权值 id标号
    void init()
    {
        l=r=p=v=id=0;
    }
}tree[maxn];
int build()
{
    //tree[0].init();
    len=1;
    stk[1]=1;
    for(int i=2;i<=n;i++)
    {
        while((len>0)&&(tree[stk[len]].v<tree[i].v))len--;
        if(len)
        {
            tree[i].p=stk[len];
            tree[tree[stk[len]].r].p=i;
            tree[i].l=tree[stk[len]].r;
            tree[stk[len]].r=i;
        }
        else
        {
            tree[stk[1]].p=i;
            tree[i].l=stk[1];
        }
        stk[++len]=i;
    }
    return stk[1];
}
int a[maxn];
int sz[maxn];
void dfs(int u)
{
    if(u==0)return;
    sz[u]=1;
    dfs(tree[u].l);
    dfs(tree[u].r);
    sz[u]+=sz[tree[u].l]+sz[tree[u].r];
}


int main()
{
    invTable(maxn-5,mod,inv);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            tree[i].init();
            tree[i].v=a[i];
            tree[i].id=i;
        }
        int root=build();
        dfs(root);
        long long ans=1LL*n*inv[2]%mod;
        for(int i=1;i<=n;i++)
            ans=(ans*inv[sz[i]])%mod;
        printf("%lld\n",ans);
    }
    return 0;


}

B - Cover
大概题意,无向图最小路径覆盖,即欧拉路径的个数。
大概做法是将图分成每一个联通块来计算,并在求联通块的时候看每个点的度的奇偶性,并将奇数度数的点放入堆里面,将每两个奇数点间连一条边,然后跑fleury算法求欧拉路径即可。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
struct Edge
{
    int u,v,nxt;
    int flag;
    Edge(int u,int v,int nxt,int flag=0):u(u),v(v),nxt(nxt),flag(flag){}
    Edge(){}
}edges[maxn*4];
int head[maxn],tot;
int deg[maxn];
void addedge(int u,int v)
{
    edges[tot]=Edge(u,v,head[u]);
    head[u]=tot++;
}
void add_Edge(int u,int v)
{
    addedge(u,v);
    addedge(v,u);
    deg[u]++,deg[v]++;
}
int vis[maxn];
vector<int>odd;

void dfs(int u)
{
    vis[u]=1;
    if(deg[u]%2)odd.push_back(u);
    for(int i=head[u];~i;i=edges[i].nxt)
    {
        int v=edges[i].v;
        if(!vis[v])
            dfs(v);
    }
}
int res;
vector<int>rout[maxn];
int n,m;
void dfs2(int u)
{
    for(int i=head[u];~i;i=edges[i].nxt)if(edges[i].flag==0&&edges[i^1].flag==0)
    {
        edges[i].flag=edges[i^1].flag=1;
        int v=edges[i].v;
        dfs2(v);
        if(i>=2*m)++res;
        else rout[res].push_back((i/2+1)*(2*(i&1)-1));
    }
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        res=0;
        memset(vis,0,sizeof(vis));
        memset(deg,0,sizeof(deg));
        memset(head,-1,sizeof(head));
        for(int i=0;i<maxn;i++)rout[i].clear();
        tot=0;
        int u,v;
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d",&u,&v);
            add_Edge(u,v);
        }
        for(int i=1;i<=n;i++)if(!vis[i]&&deg[i])
        {
            odd.clear();
            dfs(i);
            if(odd.empty())
            {
                odd.push_back(i);
                odd.push_back(i);
            }
            int Size=odd.size();
            for(int i=2;i<Size-1;i+=2)
                add_Edge(odd[i],odd[i+1]);
            ++res;
            dfs2(odd[0]);
        }
        printf("%d\n",res);
        for(int i=1;i<=res;i++)
        {
            printf("%d",rout[i].size());
            for(int j=0;j<rout[i].size();j++)
                printf(" %d",rout[i][j]);
            puts("");
        }

    }
    return 0;
}

D - Matrix
广义容斥,做完这个题感觉数学真奇妙。。
这里附上别人的题解及一篇不错的文章。
题解
文章
这题卡常,需要进行xjb优化(虽然我没系统的整理优化有哪些。。

#include<bits/stdc++.h>
using namespace std;
const int maxn=3e3+5;
const int mod=998244353;
int C[maxn][maxn];

int two[maxn*maxn+10];
void init()
{
    C[0][0]=1;
    for(int i=1;i<maxn;i++)
    {
        C[i][0]=C[i][i]=1;
        for(int j=1;j<i;j++)
        {
            C[i][j]=C[i-1][j]+C[i-1][j-1];
            if(C[i][j]>=mod)C[i][j]-=mod;
        }
    }
    two[0]=1;
    for(register int i=1;i<maxn*maxn;i++)
    {
        two[i]=two[i-1]+two[i-1];
        if(two[i]>=mod)two[i]-=mod;
    }
}
int n,m,A,B;
int fa[maxn],fb[maxn];
void initf()
{
    fa[A]=1;
    for(int i=A+1;i<=n;i++)
    {
        int res=0;
        for(int j=A;j<=i-1;j++)
        {
            long long tmp=1LL*C[i-1][j-1]*fa[j];
            tmp%=mod;
            res=res+tmp;
            if(res>=mod)res-=mod;
        }
        res=mod-res;
        //res=((res-C(i-1,j-1)*fa[j]%mod)%mod+mod)%mod;
        fa[i]=res;
    }
    fb[B]=1;
    for(int i=B+1;i<=m;i++)
    {
        int res=0;
        for(int j=B;j<=i-1;j++)
        {
            long long tmp=1LL*C[i-1][j-1]*fb[j];
            tmp%=mod;
            res=res+tmp;
            if(res>=mod)res-=mod;
        }
        res=mod-res;
        //res=((res-C(i-1,j-1)*fb[j]%mod)%mod+mod)%mod;;
        fb[i]=res;
    }
}
int main()
{
    init();
    while(~scanf("%d %d %d %d",&n,&m,&A,&B))
    {
        initf();
        int ans=0;
        for(int i=A;i<=n;i++)
            for(int j=B;j<=m;j++)
            {
                long long tmp=1LL*C[n][i]*C[m][j]%mod;
                tmp=tmp*two[(n-i)*(m-j)]%mod;
                tmp=tmp*fa[i]%mod*fb[j]%mod;
                ans=ans+tmp;
                if(ans>=mod)ans-=mod;
            }
        printf("%d\n",ans);
    }
    return 0;

}

F - Problem I. Random Sequence
一道dp,以gcd的值来减少状态数。

#include<bits/stdc++.h>
using namespace std;
const int maxn=105;
const int mod=1e9+7;
int a[maxn];
int l[maxn],r[maxn];
int v[maxn];
int g[maxn][maxn];
vector<int>V[maxn];
void init()
{
    for(int i=1;i<=100;i++)
        for(int j=1;j<=100;j++)
        g[i][j]=__gcd(i,j);
    for(int i=1;i<=100;i++)
        for(int j=i;j<=100;j+=i)
            V[j].push_back(i);
}
long long dp[2][maxn][maxn][maxn];
int n,m;
long long inv(long long a,long long m)
{
    if(a==1)return 1;
    return inv(m%a,m)*(m-m/a)%m;
}

void solve()
{
    memset(dp,0,sizeof(dp));
    int w=0;
    for(int i=l[1];i<=r[1];i++)
        for(int j=l[2];j<=r[2];j++)
        for(int k=l[3];k<=r[3];k++)
        dp[w][k][g[k][j]][g[g[k][j]][i]]++;

    for(int i=4;i<=n;i++)
    {
        for(int x=1;x<=m;x++)
        {
            for(int y,yy=0;yy<V[x].size();yy++)
            {
                y=V[x][yy];
                for(int z,zz=0;zz<V[y].size();zz++)
                {
                    z=V[y][zz];
                    if(dp[w][x][y][z]==0)continue;
                    for(int j=l[i];j<=r[i];j++)
                    {
                        (dp[w^1][j][g[j][x]][g[g[j][x]][y]]+=dp[w][x][y][z]*v[g[j][z]]%mod)%=mod;
                    }
                    dp[w][x][y][z]=0;
                }
            }
        }
        w^=1;
    }
    long long ans=0;
    long long invnum=inv(m,mod);
    for(int x=1;x<=m;x++)
    {
        for(int y,yy=0;yy<V[x].size();yy++)
        {
            y=V[x][yy];
            for(int z,zz=0;zz<V[y].size();zz++)
            {
                z=V[y][zz];
                ans=(ans+dp[w][x][y][z])%mod;
            }
        }
    }
    for(int i=1;i<=n;i++)if(a[i]==0)
        ans=ans*invnum%mod;
    printf("%lld\n",ans);
}
int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            if(a[i]==0)l[i]=1,r[i]=m;
            else l[i]=r[i]=a[i];
        }
        for(int i=1;i<=m;i++)
            scanf("%d",&v[i]);
        solve();
    }
    return 0;
}

G - Problem M. Walking Plan
矩阵+分块
有一点需要注意的是它问的是至少走k步后的最小距离,假如k=199,最优次数为205,那么如果将k分成100+99,那么这样就算不到最优次数了,所以我们在预处理的时候得往后处理更多一点。

#include<bits/stdc++.h>
using namespace std;
const int maxn=55;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f;
struct M
{
    int n, m;
    ll a[maxn][maxn];
    M(int _n = 0) { n = m = _n;memset(a, inf, sizeof(a)); }
    M(int _n, int _m) { n = _n, m = _m;memset(a, inf, sizeof(a)); }
    void mem(int _n = 0) { n = m = _n, memset(a, inf, sizeof(a)); }
    void mem(int _n, int _m) { n = _n, m = _m;memset(a, inf, sizeof(a)); }
    void pri()
    {
        for (int i = 1;i <= n;i++)
        {
            for (int j = 1;j <= m;j++)cout << a[i][j] << ' ';
            cout << endl;
        }
    }
    friend M operator*(M a, M b)
    {
        M c(a.n,b.m);
        for (int k = 1;k <= a.m;k++)
            for (int i = 1;i <= a.n;i++)
                for (int j = 1;j <= b.m;j++)
                    c.a[i][j]=min(c.a[i][j],a.a[i][k]+b.a[k][j]);
        return c;
    }
    void make_I(int _n)
    {
        n = m = _n;
        memset(a, inf, sizeof(a));
        for (int i = 1;i <= n;i++)a[i][i] = 0;
    }
    void getmin(M b)
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                a[i][j]=min(a[i][j],b.a[i][j]);
    }
};
M mp;
M a[150],b[150];
int n,m;
void init()
{
    a[0].make_I(n);
    for(int i=1;i<150;i++)
        a[i]=a[i-1]*mp;
    b[0].make_I(n);
    for(int i=1;i<150;i++)
        b[i]=b[i-1]*a[100];
    for(int i=148;i>=0;i--)
    {
        a[i].getmin(a[i+1]);
        b[i].getmin(b[i+1]);
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int s,t,k;
        scanf("%d %d %d",&s,&t,&k);
        int tmp1=k/100,tmp2=k%100;
        long long ans=inf;
        for(int i=1;i<=n;i++)
           ans=min(ans,b[tmp1].a[s][i]+a[tmp2].a[i][t]);
        if(ans==inf)ans=-1;
        printf("%lld\n",ans);
    }
}

int main()
{
    //cout<<inf<<endl;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&m);
        int u,v,w;
        mp.mem(n);
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d %d",&u,&v,&w);
            mp.a[u][v]=min(mp.a[u][v],1LL*w);
        }
        init();
        //solve();
    }
    return 0;
}

H - Problem G. Depth-First Search
这题好难,问在树上按照dfs序走下来的结点顺序的字典序比给B的小的个数。
不难发现当根结点小于所给的B[1],它能走的个数也就是整颗树,这个个数好算。
所以现在我们想算当根结点等于所给的B[1]时,小于B的个数。
看着自己写的代码突然感觉很简单是怎么回事。。
找小于B的个数只需要模拟dfs走的顺序,小于当前B[d]的结点我们可以直接算出答案,然后再看等于B[d]的有多少就行了。

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
const int maxn=1e6+5;
//***************************************************
//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extgcd(long long a,long long b,long long &x,long long &y)
{
    if(a==0&&b==0)return -1;//无最大公约数
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    long long d=extgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;//返回gcd(a,b)
}
//****************求逆元******************************
//ax=1(mod n)
long long mod_reverse(long long a,long long n)
{
    long long x,y;
    long long d=extgcd(a,n,x,y);
    if(d==1)return (x%n+n)%n;
    return -1;
}
long long tab1[maxn],tab2[maxn];
void init()
{
    tab1[0]=1;
    for(int i=1; i<maxn; i++)tab1[i]=tab1[i-1]*i%mod;
    tab2[maxn-1]=mod_reverse(tab1[maxn-1],mod);
    for(int i=maxn-2; i>=0; i--)tab2[i]=tab2[i+1]*(i+1)%mod;
}
vector<int>V[maxn];
int B[maxn];
//
int rt[maxn],sz;
struct Treap
{
    #define ls T[i].ch[0]
    #define rs T[i].ch[1]
    struct treap
    {
        int ch[2],sz,val,cnt,fix;
    } T[maxn];
    void up(int i)
    {
        T[i].sz=T[i].cnt+T[ls].sz+T[rs].sz;
    }
    void Rotate(int &x,bool d)
    {
        int y=T[x].ch[d];
        T[x].ch[d]=T[y].ch[d^1];
        T[y].ch[d^1]=x;
        up(x),up(x=y);
    }
    void ins(int &i,int x)
    {
        if(!i)
        {
            i=++sz;
            T[i].fix=rand();
            T[i].sz=T[i].cnt=1;
            T[i].val=x;
            ls=rs=0;
            return;
        }
        T[i].sz++;
        if(x==T[i].val)
        {
            T[i].cnt++;
            return;
        }
        int d=x>T[i].val;
        ins(T[i].ch[d],x);
        if(T[T[i].ch[d]].fix<T[i].fix)Rotate(i,d);
    }
    void del(int &i,int x)
    {
        if(!i)return;
        if(T[i].val==x)
        {
            if(T[i].cnt>1)
            {
                T[i].cnt--,T[i].sz--;
                return;
            }
            int d=T[ls].fix>T[rs].fix;
            if(ls==0||rs==0)i=ls+rs;
            else Rotate(i,d),del(i,x);
        }
        else T[i].sz--,del(T[i].ch[x>T[i].val],x);
    }
    int rank(int i,int x)
    {
        if(!i)return 0;
        if(T[i].val>x)return rank(ls,x);
        if(T[i].val==x)return T[ls].sz+1;
        return rank(rs,x)+T[ls].sz+T[i].cnt;
    }
} treap;
//
int fa[maxn];
long long val[maxn];
int sz_node[maxn];
void dfs(int u,int pre)
{
    sz_node[u]=0;
    fa[u]=pre;
    if(pre)treap.ins(rt[pre],u);
    long long tmp=1;
    for(int v:V[u])
    {
        if(v==pre)continue;
        dfs(v,u);
        sz_node[u]++;
        tmp=tmp*val[v]%mod;
    }
    tmp=tmp*tab1[sz_node[u]]%mod;
    val[u]=tmp;
}

long long ans;
int n;
bool vis[maxn];
long long res;
bool flag;
void dfs2(int u,int &d)
{
    if(flag==0||d>n)return;
    if(sz_node[u])
    {
        int tmp=treap.rank(rt[u],B[d]-1);
        ans=(ans+1LL*tmp*res%mod*tab1[sz_node[u]-1]%mod*tab2[sz_node[u]]%mod)%mod;
        if(fa[B[d]]!=u)flag=0;
        treap.del(rt[u],B[d]);
        res=res*tab2[sz_node[u]]%mod*tab1[sz_node[u]-1]%mod;
        sz_node[u]--;
        int go=B[d];
        d++;
        dfs2(go,d);
    }
    else dfs2(fa[u],d);

}
void solve()
{
    flag=1;
    memset(vis,0,sizeof(vis));
    dfs(B[1],0);
    ans=0;
    int d=2;
    res=val[B[1]];
    dfs2(B[1],d);
    res=val[B[1]]*mod_reverse(V[B[1]].size(),mod)%mod;
    for(int i=1; i<B[1]; i++)
    {
        long long tmp=res*V[i].size()%mod;
        ans=(ans+tmp)%mod;
    }
    printf("%lld\n",ans);
}

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        sz=0;
        scanf("%d",&n);
        for(int i=1; i<=n; i++)scanf("%d",&B[i]),V[i].clear(),rt[i]=0;
        int u,v;
        for(int i=1; i<n; i++)
        {
            scanf("%d %d",&u,&v);
            V[u].push_back(v);
            V[v].push_back(u);
        }
        solve();
    }
    return 0;
}

I - Hills And Valleys
求翻转某个区间后使得非递减子序列最长。
因为它给的A的范围很小,实际上非递减子序列就是该串0123..9有多少个,那么如果要翻转的话,我们可以看成翻转0123..9。想到这就好做了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
char s[maxn];
int tab[15];
int tcnt;
int dp[maxn][15];
int al[maxn][15],ar[maxn][15];
int bl,br;
int n;
void solve2()
{
    for(int i=1;i<=n;i++)
        for(int j=0;j<12;j++)
        al[i][j]=ar[i][j]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<tcnt;j++)
        {
            dp[i][j]=dp[i-1][j];
            al[i][j]=al[i-1][j];
            ar[i][j]=ar[i-1][j];
            if(tab[j]==s[i])
            {
                dp[i][j]++;

                if(j>=bl&&j<=br&&al[i][j]==0)
                    al[i][j]=i;
                if(j>=bl&&j<=br)
                    ar[i][j]=i;
            }
            if(j&&dp[i][j]<dp[i][j-1])
            {
                dp[i][j]=dp[i][j-1];
                al[i][j]=al[i][j-1];
                ar[i][j]=ar[i][j-1];
            }
        }
    }
}

void gettab(int l,int r)
{
    tcnt=0;
    for(int i=0;i<=l;i++)tab[tcnt++]=i;
    for(int i=r;i>=l;i--)tab[tcnt++]=i;
    for(int i=r;i<10;i++)tab[tcnt++]=i;
}

void solve()
{
    tcnt=0;
    for(int i=0;i<10;i++)tab[tcnt++]=i;
    bl=br=0;
    solve2();
    int res=dp[n][tcnt-1];
    int l=1,r=1;
    for(int i=0;i<10;i++)
        for(int j=i+1;j<10;j++)
    {
        gettab(i,j);
        bl=i+1;
        br=j+1;
        solve2();
        if(res<dp[n][tcnt-1])
        {
            res=dp[n][tcnt-1];
            l=al[n][tcnt-1];
            r=ar[n][tcnt-1];
            if(l==0&&r==0)l=r=1;
        }
    }
    printf("%d %d %d\n",res,l,r);

}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        scanf("%s",s+1);
        for(int i=1;i<=n;i++)s[i]-='0';
        solve();
    }
    return 0;
}

L - sacul
lucas定理的运用。稍后再补充一些组合数公式吧。

#include<bits/stdc++.h>
using namespace std;
const int maxn=15e5;
const int mod=1e9+7;
//***************************************************
//返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
long long extgcd(long long a,long long b,long long &x,long long &y)
{
    if(a==0&&b==0)return -1;//无最大公约数
    if(b==0){x=1;y=0;return a;}
    long long d=extgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;//返回gcd(a,b)
}
//****************求逆元******************************
//ax=1(mod n)
long long mod_reverse(long long a,long long n)
{
    long long x,y;
    long long d=extgcd(a,n,x,y);
    if(d==1)return (x%n+n)%n;
    return -1;
}

//时间复杂度O(nloglogn)
bool is[maxn];
int prm[maxn], id;
long long tab1[maxn],tab2[maxn];
void init()
{
    memset(is, 1, sizeof(is));
    is[0] = is[1] = 0;
    int k = 0;
    prm[k++] = 2;
    for (int i = 4;i < maxn;i+=2)is[i] = 0;
    int e = (int)sqrt(0.0 + maxn) + 1;
    int i;
    for (i = 3;i < e;i += 2)if(is[i])
    {
        prm[k++] = i;
        for (int s = 2 * i, j = i*i;j < maxn;j += s)
            is[j] = 0;
    }
    for (;i < maxn;i += 2)if (is[i])prm[k++] = i;
    id = k;

    tab1[0]=1;
    for(int i=1;i<maxn;i++)tab1[i]=tab1[i-1]*i%mod;
    tab2[maxn-1]=mod_reverse(tab1[maxn-1],mod);
    for(int i=maxn-2;i>=0;i--)tab2[i]=tab2[i+1]*(i+1)%mod;
}

long long C(int a,int b)
{
    return tab1[a]*tab2[b]%mod*tab2[a-b]%mod;
}

long long quick_mul(long long a,long long b)
{
    long long res=1;
    while(b)
    {
        if(b%2)res=res*a%mod;
        b/=2;
        a=a*a%mod;
    }
    return res;
}

long long solve(long long a,long long b)
{
    if(a==1)
        return b;
    return a*(((quick_mul(a,b)-1)%mod+mod)%mod)%mod*mod_reverse(a-1,mod)%mod;
}

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int c,n,k;
        scanf("%d %d %d",&c,&n,&k);
        int p=prm[c-1];
        long long ans=0;
        for(int j=1;j<=k;j++)
        {
            long long tmp=C(p+j,p-1);
            ans=(ans+solve(tmp,n))%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

N - Traffic Network in Numazu
lca+动态询问树上两个点的距离

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
#define time te
//
int f[maxn];
int find(int a)
{
    return a==f[a]?a:f[a]=find(f[a]);
}
typedef pair<int,int>pii;
vector<pii>V[maxn];
void add_Edge(int u,int v,int w)
{
    V[u].push_back(pii(v,w));
    V[v].push_back(pii(u,w));
}
int su,sv,sw;
//
long long d[maxn];
int dp[maxn][20];
int dep[maxn];
int l[maxn],r[maxn];
int time;
void dfs(int u,int pre,long long dis)
{
    d[u]=dis;
    dp[u][0]=pre;
    ++time;
    l[u]=time;
    for(int i=0;i<V[u].size();i++)
    {
        pii x=V[u][i];
        int v=x.first,w=x.second;
        if(v==pre)continue;
        dep[v]=dep[u]+1;
        dfs(v,u,dis+w);
    }
    r[u]=time;
}

void rmq(int n)
{
    for(int i=1;i<20;i++)
        for(int j=1;j<=n;j++)
        {
            if((1<<i)>dep[j])continue;
            int k=dp[j][i-1];dp[j][i]=dp[k][i-1];
        }
}

int query(int x,int y)
{
    if(dep[x]>dep[y])swap(x,y);
    for(int j=19;j>=0&&dep[x]!=dep[y];j--)
    {
        if(dep[y]-(1<<j)<dep[x])continue;
        y=dp[y][j];
    }

    if(x==y)return x;
    for(int j=19;j>=0;j--)
    {
        if(dep[x]-(1<<j)<0||dp[x][j]==dp[y][j])continue;
        x=dp[x][j],y=dp[y][j];
    }
    return dp[x][0];
}
//
#define lowbit(x) x&-x
long long sum[maxn];
void add(int x,int val)
{
    while(x<maxn)
    {
        sum[x]+=val;
        x+=lowbit(x);
    }
}
long long query(int x)
{
    long long res=0;
    while(x)
    {
        res+=sum[x];
        x-=lowbit(x);
    }
    return res;
}
vector<pair<pii,int> >V2;

void update(int l,int r,int v)
{
    add(l,v);
    add(r+1,-v);
}

long long getdis(int u,int v)
{
    long long res=0;
    int lca=query(u,v);
    res+=d[u]+query(l[u]);
    res+=d[v]+query(l[v]);
    res-=2LL*(d[lca]+query(l[lca]));
    return res;
}

void outdis(int u,int v)
{
    long long res=getdis(u,v);
    res=min(res,getdis(u,su)+getdis(v,sv)+sw);
    res=min(res,getdis(u,sv)+getdis(v,su)+sw);
    printf("%lld\n",res);
}

int main()
{
    //cout<<(1<<20);
    //freopen("input.txt","r",stdin);
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int N,Q;
        scanf("%d %d",&N,&Q);
        time=0;
        V2.clear();
        memset(sum,0,sizeof(sum));
        for(int i=1;i<=N;i++)f[i]=i,V[i].clear();
        int u,v,w;
        for(int i=1;i<=N;i++)
        {
            scanf("%d %d %d",&u,&v,&w);
            int fu=find(u),fv=find(v);
            if(fu!=fv)
                add_Edge(u,v,w),f[fu]=fv;
            else
                su=u,sv=v,sw=w;
            V2.push_back(make_pair(pii(u,v),w));
        }
        if(su>sv)swap(su,sv);
        dfs(1,0,0);
        rmq(N);
        int opt,X,Y;
        while(Q--)
        {
            scanf("%d %d %d",&opt,&X,&Y);
            if(opt==0)
            {
                X--;
                pair<pii,int> tmp=V2[X];
                //tuple<int,int,int> tmp=V2[X];
                u=tmp.first.first,v=tmp.first.second,w=tmp.second;
                if(u>v)swap(u,v);
                if(u==su&&v==sv)
                    sw=Y;
                else
                {
                    if(dp[u][0]==v)
                    {
                        update(l[u],r[u],Y-w);
                        V2[X].second=Y;
                    }
                    else
                    {
                        update(l[v],r[v],Y-w);
                        V2[X].second=Y;
                    }
                }
            }
            else
                outdis(X,Y);
        }
    }
    return 0;
}

R - Rikka with Seam
dp,容易想到dp[i][j] 代表到第i行选第j列有多少状态,那么显然他是从dp[i-1][j-k]到dp[i-1][j+k]得来的,但这样算有重复的,那么我们可以再开一个数组,就叫它p[i][j] 吧,代表第i行第j列有但第i行第j-1列没有的状态数。那么显然 d p [ i ] [ j ] = d p [ i 1 ] [ j k ] + m = j k + 1 j + k p [ i 1 ] [ m ] ,那么转移的时候转移这两个数组就行了。

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int maxn=2005;
char mp[maxn][maxn];

long long p[2][maxn];
long long d[2][maxn];

long long getsum(int s,int l,int r)
{
    return ((d[s][r]-d[s][l-1])%mod+mod)%mod;
}

void solve(int n,int m,int K)
{
    int now=0;
    memset(p,0,sizeof(p));
    memset(d,0,sizeof(d));
    for(int i=1;i<=m;i++)
    {
        p[now][i]=1;
        if(mp[1][i]!=mp[1][i-1])
        {
            d[now][i]=1;
        }
        (d[now][i]+=d[now][i-1])%=mod;
    }
    for(int i=2;i<=n;i++)
    {
        now^=1;
        for(int j=1;j<=m;j++)
        {
            int nl=max(1,j-K);
            p[now][j]=(p[now^1][nl]+getsum(now^1,nl+1,min(m,j+K)))%mod;
        }
        for(int j=1;j<=m;j++)
        {
            if(mp[i][j]!=mp[i][j-1])
                d[now][j]=p[now][j];
            else if(j+K<=m)
                d[now][j]=((d[now^1][j+K]-d[now^1][j+K-1])%mod+mod)%mod;
            else d[now][j]=0;
            d[now][j]=(d[now][j]+d[now][j-1])%mod;
        }
    }
    long long res=0;
    res+=p[now][1]%mod;
    res=(res+getsum(now,2,m))%mod;
    printf("%lld\n",res);
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,K;
        scanf("%d %d %d",&n,&m,&K);
        for(int i=1;i<=n;i++)
            scanf("%s",mp[i]+1);
        solve(n,m,K);
    }
    return 0;
}

U - Problem K. Pow2
又是一道有趣的dp题。
学习到了补码的运用,补码实际上就是减法运算的结果,它和原码之间的关系为互补的,所以这题你组成了原码算成功,组成了补码也算成功。我们设dp[i][0] 为到第i位组成它原码的最小次数,dp[i][1] 为到第i位组成它补码的最小次数。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
char s[maxn],b[maxn],x[maxn];
int t[maxn];
long long sum1[maxn],sum2[maxn];
const long long inf=0x3f3f3f3f3f;
long long dp[2][maxn];
void solve(int n)
{
    for(int i=1;i<=n;i++)
    {
        s[i]-='0';
        t[i]=(s[i]^1);
    }
    t[1]++;
    for(int i=1;i<=n;i++)
    {
        if(t[i]==2)
        {
            t[i]=0;
            t[i+1]++;
        }
    }
    memset(sum1,inf,sizeof(sum1));
    memset(sum2,inf,sizeof(sum2));
    for(int i=1;i<=n;i++)
    {
        if(b[i]=='0')sum1[i]=1;
        else sum1[i]=min(sum1[i],(sum1[i-1]<<1));
        if(x[i]=='0')sum2[i]=1;
        else sum2[i]=min(sum2[i],(sum2[i-1]<<1));
    }
    memset(dp,inf,sizeof(dp));
    dp[0][0]=dp[1][0]=0;
    for(int i=1;i<=n;i++)
    {
        if(s[i]==0)
        {
            dp[0][i]=min(dp[0][i],dp[0][i-1]);
            if(sum1[i]!=inf)dp[0][i]=min(dp[0][i],dp[1][i-1]+sum1[i]);
        }
        else
        {
            if(sum1[i]!=inf)dp[0][i]=min(dp[0][i],dp[0][i-1]+sum1[i]);
        }
        if(t[i]==0)
        {
            dp[1][i]=min(dp[1][i],dp[1][i-1]);
            if(sum2[i]!=inf)dp[1][i]=min(dp[1][i],dp[0][i-1]+sum2[i]);
        }
        else
        {
            if(sum2[i]!=inf)dp[1][i]=min(dp[1][i],dp[1][i-1]+sum2[i]);
        }
    }
    printf("%lld\n",min(dp[0][n],dp[1][n]+1));
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        scanf("%s %s %s",s+1,b+1,x+1);
        solve(n);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_34921856/article/details/82656040
今日推荐