省选联测(14~20)

省选联测14 整除#

ps: ci(1/1)

我们考虑对两式同时乘以 (x1) ,发现可以将后式变为 (xm1)

f(x) 为前式,然后问题就变成了求有多少个 x 满足 f(x)(x1)mod(xm1)=0

ps: 这里多项式取模其实就是对指数取模。

发现 f(x)(x1)mod(xm1)=i=0m1aixi

我们判断 x0 是否可行时,需要不断执行一下操作:

  • aix0,则将 ai 减去 x0,将 a(i+1)modm 加上 1
  • aix0,则将 ai 加上 x0,将 a(i+1)modm 减去 1

如果最后剩下的 ai 全为 0x1x+1,那么 x0 是可行的,x0=1 特判。

我们只需要枚举 x0=max(ai)+1, 数量是 O(n) 的。每次操作会使 ai 绝对值之和减少 x01,初始值为 O(n),那么总操作次数为 O(nlogn)

可以用 setmap 一起维护,复杂度 O(nlog2n)

code
#include<bits/stdc++.h>
#define int long long
#define mk make_pair
using namespace std;
const int N=1e5+10;
int c[N],a[N];
map<int,int> k,vis;
int n,m;
struct asd{
    int x,k;
}st[N];
set<pair<int,int>>s;
int top;
bool check(int x){
    int top=0;
    while(1){
        auto ip=s.begin(),it=s.end();
        it--;
        int op=0;
        while(k[(*it).second]>=x){
            op=1;
            int sx=it -> first;
            int sy=it -> second;
            if(!vis[sy]){
                vis[sy]=1;
                st[++top]={sy,k[sy]};
            }
            s.erase(mk(k[sy%m],sy%m));
            k[sy%m]-=x;
            s.insert(mk(k[sy%m],sy%m));
            if(!vis[(sy+1)%m]){
                vis[(sy+1)%m]=1;
                st[++top]={(sy+1)%m,k[(sy+1)%m]};
            }
            s.erase(mk(k[(sy+1)%m],(sy+1)%m));
            k[(sy+1)%m]++;
            s.insert(mk(k[(sy+1)%m],(sy+1)%m));
        }
        while(k[(*ip).second]<=-x){
            op=1;
            int sy=ip -> second;
            if(!vis[sy]){
                vis[sy]=1;
                st[++top]={sy,k[sy]};
            }
            s.erase(mk(k[sy%m],sy%m));
            k[sy%m]+=x;
            s.insert(mk(k[sy%m],sy%m));
            if(!vis[(sy+1)%m]){
                vis[(sy+1)%m]=1;
                st[++top]={(sy+1)%m,k[(sy+1)%m]};
            }
            s.erase(mk(k[(sy+1)%m],(sy+1)%m));
            k[(sy+1)%m]--;
            s.insert(mk(k[(sy+1)%m],(sy+1)%m));
        }
        if(!op) break;
    }
    auto it=s.begin(),ip=s.end();
    ip--;
    int sx=(*it).first,sy=(*ip).first;
    for(int i=1;i<=top;i++){
        int s1=st[i].x,s2=st[i].k;
        vis[s1]=0;
        s.erase(mk(k[s1],s1));
        k[s1]=s2;
        s.insert(mk(k[s1],s1));
    }
    if(sx!=sy) return 0;
    else{
        if(sx==x-1) return 1;
        if(sx==-x+1) return 1;
        if(sx==0) return 1;
        return 0;
    }
}
signed main(){
    freopen("div.in","r",stdin);
    freopen("div.out","w",stdout);
    int T;
    scanf("%lld",&T);
    while(T--){
        scanf("%lld%lld",&n,&m);
        k.clear();
        s.clear();
        int sum=0;
        for(int i=0;i<n;i++){
            scanf("%lld%lld",&c[i],&a[i]);
            sum+=c[i];
        }
        int mx=0;
        int op=0;
        for(int i=0;i<n;i++){
            s.erase(mk(k[(a[i]+1)%m],(a[i]+1)%m));
            k[(a[i]+1)%m]+=c[i];
            s.insert(mk(k[(a[i]+1)%m],(a[i]+1)%m));
            s.erase(mk(k[a[i]%m],a[i]%m));
            k[a[i]%m]-=c[i];
            s.insert(mk(k[a[i]%m],a[i]%m));
        }
        auto it=s.end();
        it--;
        mx=(*it).first;
        if((*it).first==(*s.begin()).first && (*it).first==0){
            printf("-1\n");
            continue;
        }
        int ans=0;
        if(sum%m==0) ans++;
        for(int i=2;i<=mx;i++){
            if(check(i)) ans++;
        }
        printf("%lld\n",ans);
    }
}

省选联测14 词典#

ps: n1e15

我们考虑对这个建一棵 trie 树,然后考虑我们选的字符串就是根到某个叶子结点,对答案的贡献就是这条边下面有多少叶子节点,然后 0 的限制就是不能连着选 0。设 g(x) 表示某个点的父亲边为 0 且有 x 个儿子, f(x) 表示某个点的父亲边为 1 且有 x 个儿子。那么考虑转移 g(x),因为有 0 的限制只能从 f(x) 转移过来,也就是 g(x)=f(x)+w(x);对于 f(x) 则有 f(x)=min(1k<x){f(xk)+g(k)}+w(x),也就是:

f(x)=min(1k<x){f(xk)+f(k)+w(k)}+w(x)

发现 w(k) 是凸函数,则 f(x) 也为凸函数,比较满足四边形不等式,所以具有决策单调性,那么可以 O(n) 做。

因为 w(n)O(nlogn) 级别的,那么 f(n)O(nlog2n) 级别的,f(x)f(x1)log2n 级别的,那么可以考虑对于每个 v=f(x)f(x1),求出最大的 x,查询时根据二分出起点,根据斜率算就可以。

我们考虑倍增,对于决策点 k,他肯定是 x2,所以我们倍增的时候需要乘 1.5。我们求 f(n) 时可以将三分下界设为 n/3

复杂度 O(log5n)

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e7;
const int inf=3e15;
int w[N+5];
int f[N+5];
int ask(int p,int n){
    int ans=f[p]+f[n-p];
    if(p>1) ans+=w[p];
    return ans;
}
int br[N],val[N],ads[N],cnt=0;
int F(int x){
    if(x<=N) return f[x];
    int pos=lower_bound(br+1,br+cnt+1,x)-br;
    return val[pos-1]+ads[pos]*(x-br[pos-1]);
}
int g[N],sumg[N],tp=0;
int W(int x){
    int pos=lower_bound(g+1,g+tp+1,x)-g;
    if(g[pos]>x) pos--;
    return sumg[pos]+(pos-1)*(x-g[pos]+1)+x;
}
int query(int x){
    if(x<=N) return f[x];
    int l=max(x/3,x-br[cnt]),r=(x+1)/2;
    int ans=LONG_LONG_MAX;
    while(r-l>2){
        int mid1=l+(r-l)/3,mid2=r-(r-l)/3;
        int ans1=F(mid1)+F(x-mid1)+W(mid1)+W(x);
        int ans2=F(mid2)+F(x-mid2)+W(mid2)+W(x);
        if(ans1<=ans2){
            r=mid2;
            ans=min(ans,ans1);
        }
        else{
            l=mid1;
            ans=min(ans,ans2);
        }
    }
    return ans;
}
void init(){
    int kl=1;
    while(kl<=inf){
        g[++tp]=kl;
        if(tp>1) sumg[tp]=sumg[tp-1]+(kl-g[tp-1])*(tp-2);
        kl*=2;
    }

    for(int i=1;i<=N;i++) w[i]=w[i-1]+(int)(1+log2(i));
    f[1]=1;
    int p=1;
    for(int i=2;i<=N;i++){
        while(p+1<i && ask(p,i)>ask(p+1,i)) p++;
        f[i]=ask(p,i)+w[i];
    }
    int dels=f[N]-f[N-1];
    int ed=N;
    br[++cnt]=ed,ads[cnt]=dels,val[cnt]=f[ed];
    int sum=0;
    while(ed<=inf){
        sum++;
        int stp=ed+1,nex=stp;
        dels=query(stp)-F(stp-1);
        while(stp<=inf){
            nex=stp*3/2;
            int tmp=query(nex);
            if(tmp-query(nex-1)!=dels) break;
            stp=nex;
        }
        for(int i=50;i>=0;i--){
            if(stp+(1ll<<i)<=nex){
                int tmp=stp+(1ll<<i);
                if(query(tmp)-query(tmp-1)==dels) stp=tmp;
            }
        }
        br[++cnt]=stp,ads[cnt]=dels;
        br[cnt]=stp,val[cnt]=query(stp);
        ed=stp;
    }
}
int write(int x){
    if(x<=N) return f[x];
    else return F(x);
}
signed main(){
    freopen("dictionary.in","r",stdin);
    freopen("dictionary.out","w",stdout);

    int T;
    scanf("%lld",&T);
    init();
    while(T--){
        int n;
        scanf("%lld",&n);
        printf("%lld\n",write(n));
    }
}

省选联测17#

树上的棋局#

首先考虑 SG 函数等于啥,我们可以发现一个点的 SG 函数只与这个点子树中距离最远的点相关,并且为距离减一。可以归纳得出,叶子结点为 0,那么叶子的父亲为 1,父亲的父亲为 2,因为包含一下所有状态。并且还发现每个节点相互独立,所以整棵树的 SG 值为全部的异或起来。

我们发现每个点对答案的贡献要么是距离最大值,要么是距离次大值,因为一个节点只有一个父亲。而且最大值对应的点一定是直径的一个端点,且经过直径的中点。那么我们发现,只有根到直径中点的路径上的点最大值才会产生贡献,其它点都是次大值产生贡献。

考虑修改和换根怎么做,我们维护四个值:奇数最大,奇数次大,偶数最大,偶数次大。显然偶数都为 0,那么修改就是将奇数和偶数翻转,换根就是将最大与次大翻转,线段树树剖维护。

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2*1e5+10;
int head[N],nex[N*2],ver[N*2],tot=0;
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
}
int fa[N],siz[N],dfn[N],rk[N],top[N],dep[N],son[N],num=0;
int mx[N],mxx[N],px[N],pxx[N];
int maxdep=(1ll<<50),pot1,pot2;
void dfs_1(int x,int fat){
    mx[x]=px[x]=0;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fat) continue;
        dfs_1(y,x);
        if(mx[x]<mx[y]+1){
            mx[x]=mx[y]+1;
            px[x]=y;
        }
    }
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fat || y==px[x]) continue;
        if(mxx[x]<mx[y]+1){
            mxx[x]=mx[y]+1;
            pxx[x]=y;
        }
    }
}
void dfs_2(int x,int fat){
    if(maxdep>mx[x]){
        maxdep=mx[x];
        pot1=x;
        pot2=0;
    }
    else if(maxdep==mx[x]){
        pot2=x;
    }
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fat) continue;
        int w;
        if(pxx[x]!=y) w=mxx[x]+1;
        if(px[x]!=y) w=mx[x]+1;
        if(mx[y]<w){
            mxx[y]=mx[y];
            pxx[y]=px[y];
            mx[y]=w;
            px[y]=x;
        }
        else if(mxx[y]<w){
            mxx[y]=w;
            pxx[y]=x;
        }
        dfs_2(y,x);
    }
}
void dfs1(int x,int fat){
    fa[x]=fat;
    dep[x]=dep[fat]+1;
    siz[x]=1;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fat) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(siz[son[x]]<siz[y]) son[x]=y;
    }
}
void dfs2(int x,int tp){
    top[x]=tp;
    dfn[x]=++num;
    rk[num]=x;
    if(son[x]) dfs2(son[x],tp);
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y!=fa[x] && y!=son[x]) dfs2(y,y);
    }
}
int ask_lca(int x,int y){
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>dep[fy]){
            x=fa[fx],fx=top[x];
        }else{
            y=fa[fy],fy=top[y];
        }
    }
    if(dep[x]<dep[y]) return x;
    else return y;
}
int ask_dist(int x,int y){
    if(!x || !y) return (1ll<<50);
    int lca=ask_lca(x,y);
    return dep[x]+dep[y]-2*dep[lca];
}
struct Segment{
    struct tree{
        int l,r,ans0,ans1,cnt0,cnt1,lazy1,lazy2;
    }tr[N*4];
    void pushup(int p){
        tr[p].ans1=tr[p*2].ans1^tr[p*2+1].ans1;
        tr[p].ans0=tr[p*2].ans0^tr[p*2+1].ans0;
        tr[p].cnt1=tr[p*2].cnt1^tr[p*2+1].cnt1;
        tr[p].cnt0=tr[p*2].cnt0^tr[p*2+1].cnt0;
    }
    void pushdown(int p){
        if(tr[p].lazy1){ // 最远 和 次远 交换 
            tr[p].lazy1=0;
            tr[p*2].lazy1^=1,tr[p*2+1].lazy1^=1;
            swap(tr[p*2].ans0,tr[p*2].cnt0);
            swap(tr[p*2].ans1,tr[p*2].cnt1);
            swap(tr[p*2+1].ans0,tr[p*2+1].cnt0);
            swap(tr[p*2+1].ans1,tr[p*2+1].cnt1);
        }
        if(tr[p].lazy2){ //  奇数偶数 交换 
            tr[p].lazy2=0;
            tr[p*2].lazy2^=1,tr[p*2+1].lazy2^=1;
            swap(tr[p*2].ans0,tr[p*2].ans1);
            swap(tr[p*2].cnt0,tr[p*2].cnt1);
            swap(tr[p*2+1].ans0,tr[p*2+1].ans1);
            swap(tr[p*2+1].cnt0,tr[p*2+1].cnt1);
        }
    }
    void build(int p,int l,int r){
        tr[p].l=l,tr[p].r=r;
        tr[p].lazy1=tr[p].lazy2=0;
        if(l==r){
            tr[p].ans0=0,tr[p].ans1=mxx[rk[l]];
            tr[p].cnt0=0,tr[p].cnt1=mx[rk[l]];
            return;
        }
        int mid=(l+r)/2;
        build(p*2,l,mid);
        build(p*2+1,mid+1,r);
        pushup(p);
    }
    void change_1(int p,int l,int r){ // 最远 和 次远 交换 
        if(tr[p].l>=l && tr[p].r<=r){
            tr[p].lazy1^=1;
            swap(tr[p].ans0,tr[p].cnt0);
            swap(tr[p].ans1,tr[p].cnt1);
            return;
        }
        pushdown(p);
        int mid=(tr[p].l+tr[p].r)/2;
        if(l<=mid) change_1(p*2,l,r);
        if(r>mid) change_1(p*2+1,l,r);
        pushup(p);
    }
    void change_2(int p,int l,int r){ //  奇数偶数 交换 
        if(tr[p].l>=l && tr[p].r<=r){
            tr[p].lazy2^=1;
            swap(tr[p].ans0,tr[p].ans1);
            swap(tr[p].cnt0,tr[p].cnt1);
            return;
        }
        pushdown(p);
        int mid=(tr[p].l+tr[p].r)/2;
        if(l<=mid) change_2(p*2,l,r);
        if(r>mid) change_2(p*2+1,l,r);
        pushup(p);
    }
}T;
void solve1(int x,int y){ // 最远 和 次远 交换 
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>dep[fy]){
            T.change_1(1,dfn[fx],dfn[x]);
            x=fa[fx],fx=top[x];
        }
        else{
            T.change_1(1,dfn[fy],dfn[y]);
            y=fa[fy],fy=top[y];
        }
    }
    if(dfn[x]<dfn[y]) T.change_1(1,dfn[x],dfn[y]);
    else T.change_1(1,dfn[y],dfn[x]);
}
void solve2(int x,int y){ //  奇数偶数 交换 
    int fx=top[x],fy=top[y];
    while(fx!=fy){
        if(dep[fx]>dep[fy]){
            T.change_2(1,dfn[fx],dfn[x]);
            x=fa[fx],fx=top[x];
        }
        else{
            T.change_2(1,dfn[fy],dfn[y]);
            y=fa[fy],fy=top[y];
        }
    }
    if(dfn[x]<dfn[y]) T.change_2(1,dfn[x],dfn[y]);
    else T.change_2(1,dfn[y],dfn[x]);
}
int ask(int x,int y){
    int fx=top[x];
    while(dep[fa[fx]]>dep[y]){
        x=fa[fx],fx=top[x];
    }
    if(dep[fx]>dep[y]) x=fx;
    while(fa[x]!=y) x=fa[x];
    return x;
}
signed main(){
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);
    int n,m;
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<n;i++){
        int x,y;
        scanf("%lld%lld",&x,&y);
        add(x,y),add(y,x);
    }
    dfs1(1,0);
    dfs2(1,1);
    dfs_1(1,0);
    dfs_2(1,0);
    T.build(1,1,n);
    int rt=1;
    int las;
    if(ask_dist(rt,pot1)<ask_dist(rt,pot2)){
        las=pot1;
        solve1(rt,pot1);
    }
    else{
        las=pot2;
        solve1(rt,pot2);
    }
    for(int i=1;i<=m;i++){
        int op,x;
        scanf("%lld",&op);
        solve1(rt,las);
        if(op==1){
            int u,v;
            scanf("%lld%lld%lld",&u,&v,&x);
            solve2(u,v);
        }
        else{
            int u;
            scanf("%lld%lld",&u,&x);
            int lca=ask_lca(rt,u);
            if(rt==u) T.change_2(1,1,n);
            else if(lca==u){
                int k=ask(rt,u);
                T.change_2(1,1,n);
                T.change_2(1,dfn[k],dfn[k]+siz[k]-1);
            }
            else{
                T.change_2(1,dfn[u],dfn[u]+siz[u]-1);
            }
            rt=x;
        }
        if(ask_dist(x,pot1)<ask_dist(x,pot2)){
            las=pot1;
            solve1(x,pot1);
        }
        else{
            las=pot2;
            solve1(x,pot2);
        }
        printf("%lld\n",T.tr[1].ans1);
        rt=x;
    }
}

社会黄油飞#

对于限制 e(V)|V|1lim 变换一下就有 e(V)|V|limlim 那么发现这是最大权闭合子图问题,就是让左式最大,每个点权值为 lim ,每条边权值为 1,选一条边会强制选两个点。

为了不存在啥都不选的情况,我们每回强制选一个,可以先把这条边的流退回去,然后再跑网络流。

code
// ubsan: undefined
// accoders
// socialbutterfly 社会黄油飞
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=6*1e3+10;
const int E=3*1e6+10;
const int inf=(1ll<<50);
struct edge{
    int u,v;
}e[N];
int head[N*2],nex[E],ver[E],edge[E],tot=1;
void add(int x,int y,int w){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot,edge[tot]=w;
    ver[++tot]=x,nex[tot]=head[y],head[y]=tot,edge[tot]=0;
}
int st,ed;
queue<int> q;
int dep[E],cur[E];
int bfs(){
    memset(dep,0,sizeof(int)*(ed+1));
    dep[st]=1;
    q.push(st);
    while(!q.empty()){
        int x=q.front();
        q.pop();
        for(int i=head[x];i;i=nex[i]){
            int y=ver[i];
            if(dep[y] || edge[i]<=0) continue;
            dep[y]=dep[x]+1;
            q.push(y);
        }
    }
    if(dep[ed]) return 1;
    else return 0;
}
int dfs(int x,int flow){
    if(x==ed) return flow;
    for(int i=cur[x];i;i=nex[i]){
        cur[x]=i;
        int y=ver[i];
        if(dep[y]==dep[x]+1 && edge[i]>0){
            int d=dfs(y,min(flow,edge[i]));
            if(d>0){
                edge[i]-=d;
                edge[i^1]+=d;
                return d;
            }
        }
    }
    return 0;
}
int ans=0;
int Dinic(){
    while(bfs()){
        for(int i=1;i<=ed;i++) cur[i]=head[i];
        while(int d=dfs(st,inf)){
            ans+=d;
        }
    }
    return ans;
}


signed main(){
    // freopen("in.in","r",stdin);
    freopen("socialbutterfly.in","r",stdin);
    freopen("socialbutterfly.out","w",stdout);
    int n,m,lim;
    scanf("%lld%lld%lld",&n,&m,&lim);
    st=n+m+1,ed=st+1;
    int sum=m;
    for(int i=1;i<=m;i++){
        scanf("%lld%lld",&e[i].u,&e[i].v);
        add(st,i,1);
        add(i,m+e[i].u,inf);
        add(i,m+e[i].v,inf);
    }
    for(int i=1;i<=n;i++) add(m+i,ed,lim);
    int getans=0;
    for(int p=1;p<=n;p++){
        int tmp;
        for(int i=head[m+p];i;i=nex[i]){
            int y=ver[i];
            if(y==ed){
                edge[i]=0;
                edge[i^1]=0;
                tmp=i;
                break;
            }
        }
        for(int i=head[m+p];i;i=nex[i]){
            int y=ver[i];
            if(y<=m){
                int flow=edge[i];
                edge[i]=0;
                edge[i^1]=inf;
                for(int j=head[y];j;j=nex[j]){
                    int k=ver[j];
                    if(k==st){
                        edge[j]-=flow;
                        edge[j^1]+=flow;
                        ans-=flow;
                        break;
                    }
                }
            }
        }
        if(sum-Dinic()>0){
            getans=1;
            break;
        }
        edge[tmp]=lim;
    }
    if(getans) printf("Yes\n");
    else printf("No\n");
}

省选模拟20#

装备#

我们讲所有的 aibi 连边,因为每个值只会有一条出边,发现最后的有向图是内向基环树森林。因为 Ai<=n,所以从后往前贪心是对的。考虑贪心的过程,我们想要交换的话,其实就是翻转一些边的方向。发现如果一个弱联通中存在一个环,那么这个联通里面的值就不会改变,否则我们维护那一个空闲的点在哪,然后加入要操作的话直接从那个点到它的路径方向翻转,对于翻转过的路径是不能再次翻转的。那个空闲的节点一定只会不断往子树中走,所以可以用 dfs 序判断是否可以进行操作,对于不更改的也需要将其子树中的边的方向固定。复杂度 O(n)

code
#include<bits/stdc++.h>
using namespace std;
const int N=2*1e5+5;
int head[N],nex[N*2],ver[N*2],tot=1;
bool flat[N*2],vis[N];
int a[N],b[N];
void add(int x,int y){
    ver[++tot]=y,nex[tot]=head[x],head[x]=tot;
    flat[tot]=1;
    ver[++tot]=x,nex[tot]=head[y],head[y]=tot;
}
int c[N],cnt[N],rt[N],col[N],num=0,able[N];
int dfn[N],siz[N],idx=0,dep[N],f[N];
int ans[N],anss[N];
int sum=0;
unordered_map<int,int> rk[N],chan[N];
void dfs3(int x,int fa){
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        if(flat[i]) chan[x][y]=1;
        else chan[y][x]=1;
        dfs3(y,x);
    }
}
void dfs2(int x,int fa){
    f[x]=fa;
    dep[x]=dep[fa]+1;
    dfn[x]=++idx;
    siz[x]=1;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(y==fa) continue;
        dfs2(y,x);
        siz[x]+=siz[y];
    }
}
void dfs(int x,int fa){
    col[x]=num;
    vis[x]=1;
    for(int i=head[x];i;i=nex[i]){
        int y=ver[i];
        if(flat[i]) cnt[x]++;
        if(y==fa) continue;
        if(vis[y]){
            able[num]=1;
            continue;
        }
        dfs(y,x);
    }
    if(!cnt[x]) rt[num]=x;
}
signed main(){
    freopen("equipment.in","r",stdin);
    freopen("equipment.out","w",stdout);
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++) scanf("%d",&c[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&a[i],&b[i]);
        add(a[i],b[i]);
        rk[a[i]][b[i]]=i;
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            ++num;
            dfs(i,0);
            if(!able[num])dfs2(rt[num],0);
        }
    }
    for(int p=m;p>=1;p--){
        int colr=col[a[p]];
        if(able[colr]) continue;
        if(chan[a[p]][b[p]] || chan[b[p]][a[p]]) continue;
        if(c[a[p]]==c[b[p]]) continue;
        if(c[a[p]]>c[b[p]]){
            chan[a[p]][b[p]]=1;
            dfs2(a[p],b[p]);
        }
        if(c[a[p]]<c[b[p]]){
            if(dfn[b[p]]>=dfn[rt[colr]] && dfn[b[p]]<=dfn[rt[colr]]+siz[rt[colr]]-1 && k>=dep[a[p]]-dep[rt[colr]]){
                int tmp=a[p];
                int sum1=0;
                while(1){
                    anss[++sum1]=rk[tmp][f[tmp]];
                    chan[f[tmp]][tmp]=1;
                    tmp=f[tmp];
                    if(tmp==rt[colr]) break;
                }
                for(int i=sum1;i>=1;i--) ans[++sum]=anss[i];
                k-=(dep[a[p]]-dep[rt[colr]]);
                rt[colr]=a[p];
            }
        }
    }
    printf("%d\n",sum);
    for(int i=1;i<=sum;i++){
        printf("%d\n",ans[i]);
    }
}

比赛#

首先设 dpl,r,x 表示 区间 (l,r)x 获胜的概率,那么可以枚举区间的分界线,然后枚举右区间谁会胜出,然后转移,复杂度 O(n5)

然后考虑优化,发现最后获胜的是 x,且 x 的左右互不影响,那么 x 的获胜概率就是 (l,x)x 获胜概率乘上 (x,r)x 获胜概率。那么可以维护 fl,r,0/1 表示 (l,r) 区间 l/r 获胜概率。那么可以枚举 l,r,然后枚举分界线 k,再然后枚举另一个获胜的的点 y,复杂度 n4

再优化,我们考虑先枚举 y,那么我们需要求出来最后只剩 x,y (获胜)的概率是啥,然后我们就维护 gx,y=i=xi<yfx,i,1fi+1,y,0,这个可以 O(n) 处理,然后就可以直接枚举 y,复杂度 O(n3)

code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=505;
const int mod=998244353;
int dp[N][N][N];
int f[N][N][2],g[N][N];
int a[N];
int inv[N][N];
int qpow(int x,int p){
    int ans=1;
    while(p){
        if(p&1) ans=(ans*x)%mod;
        x=(x*x)%mod;
        p>>=1;
    }
    return ans;
}
signed main(){
    freopen("tournament.in","r",stdin);
    freopen("tournament.out","w",stdout);
    int n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        f[i][i][0]=f[i][i][1]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            inv[i][j]=qpow(a[i]+a[j],mod-2);
        }
    }
    for(int len=2;len<=n;len++){
        for(int l=1;l+len-1<=n;l++){
            int r=l+len-1;
            for(int k=l;k<r;k++) g[l][r]=(g[l][r]+f[l][k][0]*f[k+1][r][1]%mod)%mod;
            for(int x=l;x<=r;x++){
                if(x>l) f[l][r][0]=(f[l][r][0]+g[l][x]*inv[l][x]%mod*a[l]%mod*f[x][r][0]%mod)%mod;
                if(x<r) f[l][r][1]=(f[l][r][1]+g[x][r]*inv[r][x]%mod*a[r]%mod*f[l][x][1]%mod)%mod;
            }
            int px=qpow(r-l,mod-2);
            f[l][r][0]=f[l][r][0]*px%mod;
            f[l][r][1]=f[l][r][1]*px%mod;
        }
    }
    for(int i=1;i<=n;i++){
        printf("%lld\n",f[1][i][1]*f[i][n][0]%mod);
    }
}

作者:bloss

出处:https://www.cnblogs.com/jinjiaqioi/p/17975647

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   _bloss  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu