2019多校 7.29

6605 Yukikaze and Demons

exgcd+点分治
点分治统计路径,exgcd求出 x ∗ 1 0 l e n + n u m ≡ 0 ( m o d   k ) x*10^{len}+num \equiv 0 (mod\ k) x10len+num0(mod k)
数组 O ( 1 ) O(1) O(1)查询即可
复杂度问题在于x的个数,因为 1 0 l e n 10^{len} 10len固定个人感觉是没办法卡的

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;

const int M=5e5+5;

int n,m;
int node_num;
int best_;
int f[M];
int size[M];
int bin[M];
int sta[M];
char s[M];
int e_size,head[M];
s64 ans;

bool vis[M];


struct edge{
    int v,nxt;
}e[M*2];

void e_add(int u,int v) {
    e[++e_size]=(edge){v,head[u]};
    head[u]=e_size;
}


int exgcd(int&x,int&y,int a,int b) {
    if(!b) return x=1,y=0,a;
    int gcd=exgcd(x,y,b,a%b);

    int t=x;
    x=y;
    y=t-a/b*y;
    return gcd;
}

void dp(int x,int fa) {
    f[x]=0;
    size[x]=1;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(v==fa||vis[v]) continue;
        dp(v,x);
        size[x]+=size[v];
        f[x]=max(f[x],size[v]);
    }
    f[x]=max(f[x],node_num-size[x]);
    if(!best_||f[best_]>f[x]) best_=x; 
}

void add_(int x,int fa,int k,int sum,int t,int ha) {
    sum=(sum+1ll*k*s[x]%m)%m;
    sta[sum]+=t;

    if(ha) ans+= sum==0;
    //cout<<x<<" "<<sum<<endl;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]||v==fa) continue;

        add_(v,x,10ll*k%m,sum,t,ha);
    }
}

void calc_(int x,int fa,int dep,int sum) {

    sum=(sum+s[x])%m;
    size[x]=1;


    //hardest
    int xx,yy;
    int x_,y_;
    int gcd=exgcd(xx,yy,bin[dep],-m);


    if(sum%gcd==0) {
        xx=-sum/gcd*xx;
        yy=-sum/gcd*yy;
        if(gcd<0) gcd=-gcd;
        x_=m/gcd,y_=bin[dep]/gcd;


    //cout<<"-_-"<<x<<" "<<xx<<" "<<yy<<" "<<bin[dep]<<" "<<-m<<" "<<gcd<<endl;


        //cout<<x_<<" "<<bin[dep]<<endl;
        if(xx<0) {
            int c=(-xx-1)/x_+1;
            xx+=c*x_,yy+=c*y_;
        }
        if(xx>0) {
            int c=xx/x_;
            xx-=c*x_,yy-=c*y_;
        }
        if(yy<0) {
            int c=(-yy-1)/y_+1;
            xx+=c*x_,yy+=c*y_;
        }

        while (xx<m) {

        //cout<<"-_-"<<x<<" "<<xx<<" "<<sta[xx]<<endl;
            ans+=sta[xx],xx+=x_;
        }
    }


    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]||v==fa) continue;
        calc_(v,x,dep+1,10*sum%m);
        size[x]+=size[v];
    }

}

void solve(int x) {

    //cout<<"-_-"<<x<<endl;

    ++sta[s[x]];
    ans+= s[x]==0;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]) continue;

        add_(v,x,10,s[x],1,1);
    }


    //cout<<"-_-"<<endl;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]) continue;
        add_(v,x,10,s[x],-1,0);
        calc_(v,x,1,0);
        add_(v,x,10,s[x],1,0);
        //cout<<"-_-"<<x<<" "<<v<<" "<<ans<<endl;
    }

   // while(1);

    --sta[s[x]];
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]) continue;
        add_(v,x,10,s[x],-1,0);
    }


    //cout<<x<<" "<<ans<<endl;

    vis[x]=1;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(vis[v]) continue;

        node_num=size[v];
        best_=0;
        dp(v,x);
        solve(best_);
    }
}

void divid_() {
    best_=0;
    node_num=n;
    dp(1,0);


    solve(best_);
}

int main() {
    //freopen("a.txt","r",stdin);
    //freopen("a.out","w",stdout);

    int test_;
    cin>>test_;


    while (test_--) {
        scanf("%d%d%s",&n,&m,s+1);
        rep(i,1,n) s[i]-='0',s[i]%=m;

        rep(i,2,n) {
            int x,y;
            scanf("%d%d",&x,&y);
            e_add(x,y),e_add(y,x);
        }

        bin[0]=1;
        rep(i,1,n) bin[i]=10*bin[i-1]%m;

        divid_();

        printf("%lld\n",ans);

        ans=0;
        e_size=0;
        rep(i,1,n) vis[i]=head[i]=0;
    }
}

6608 Fansblog

素数之间间距不会很大,暴力枚举判断即可
( p r i m e − 1 ) ! ≡ 1 ( m o d   p r i m e ) (prime-1)! \equiv 1 (mod\ prime) (prime1)!1(mod prime)
除掉暴力枚举的不是素数的数即可

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;
typedef long long s64;


const int M=1e7+5;

s64 mod;
int prime[M];
bool not_prime[M];


bool check(s64 x) {
    for(int i=1;i<=prime[0] && 1ll*i*i<=x;++i) 
        if(x%prime[i]==0) return 0;
    return 1;
}

s64 mul(s64 a,s64 b,s64 mod) {
    s64 ans=a*b-(s64)((long double)a/mod*b+0.5)*mod;
    return ans<0?ans+mod:ans;
}

s64 qmul(s64 a,s64 k,s64 mod) {
    s64 ans=1;
    for(;k;k>>=1,a=mul(a,a,mod)) if(k&1) ans=mul(a,ans,mod);
    return ans;
}

int main() {
    //freopen("a.txt","r",stdin);
    //freopen("a.out","w",stdout);

    rep(i,2,10000000) {
        if(!not_prime[i]) prime[++prime[0]]=i;
        rep(j,1,prime[0]) {
            if(i*prime[j]>10000000) break;
            not_prime[i*prime[j]]=1;
            if(i%prime[j]==0) break; 
        }
    }

    int test_;
    cin>>test_;

    while (test_--) {
        cin>>mod;

        s64 x=mod-1,ans=mod-1;
        while (!check(ans)) {
            x=mul(x,qmul(ans,mod-2,mod),mod);
            --ans;
        }
        cout<<x<<endl;
    }
}

6611 K Subsequence

费用流
优化建图 a i ≤ a j ≤ a k a_{i} \leq a_{j} \leq a_{k} aiajak只建边 ( i , k ) (i,k) (i,k)
这样会错,再加上 ( i , i ′ , f l o w = m , c o s t = 0 ) (i,i&#x27;,flow=m,cost=0) (i,i,flow=m,cost=0)即可
注意pre[S]要清空
只有一组数据不清空是可以的,因为pre[S]从来都是0
但是多组数据pre[S]会被上一组修改
就TLE了
感觉oi和acm还真的是不太一样啊

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;


const int M=5e3+5;
int n,m;
int a[M];
int S,SS,T;
int e_size,head[M];
bool inq_[M];
int dis[M],pre[M];

struct edge{
    int u,v,w,c,nxt;
}e[M*100];

void e_add(int u,int v,int w,int c) {
    e[++e_size]=(edge){u,v,w,c,head[u]};
    head[u]=e_size;
}

void insert(int u,int v,int w,int c) {
    e_add(u,v,w,c),e_add(v,u,0,-c);
}

void spfa() {
    memset(dis,-0x3f,sizeof(dis));
    queue <int> q;
    dis[S]=0;
    q.push(S);
    pre[S]=0;
    while (!q.empty()) {
        int r=q.front();
        q.pop();
        inq_[r]=0;
        for(int i=head[r];i;i=e[i].nxt) {
            int v=e[i].v;
            if(dis[v]<dis[r]+e[i].c&&e[i].w) {
                pre[v]=i;
                dis[v]=dis[r]+e[i].c;
                if(!inq_[v]) inq_[v]=1,q.push(v);
            }
        }
    }
}


void feiyong() {
    int ans=0;
    while (spfa(),dis[T]>0) {
        int t=1e9;
        for(int i=pre[T];i;i=pre[e[i].u]) t=min(t,e[i].w);
        ans+=t*dis[T];
        for(int i=pre[T];i;i=pre[e[i].u]) e[i].w-=t,e[i^1].w+=t;
    }
    printf("%d\n",ans);
}

int main() {
    //freopen("a.txt","r",stdin);

    int test_;
    cin>>test_;
    while (test_--) {
        scanf("%d%d",&n,&m);
        rep(i,1,n) scanf("%d",a+i);


        e_size=1;
        memset(head,0,sizeof(head));


        S=2*n+5,SS=S+1,T=SS+1;
        insert(S,SS,m,0);
        rep(i,1,n) insert(SS,i,m,0),insert(i,i+n,1,a[i]),insert(i,i+n,m,0),insert(i+n,T,m,0);

        rep(i,1,n) {
            int pre=2e5;
            rep(j,i+1,n) if(a[i]<=a[j]&&pre>a[j]) {
                pre=a[j];
                insert(i+n,j,m,0);
            }
        }
        feiyong();
    }
}

6613 Squrirrel

直接树形dp
f [ i ] [ 0 / 1 ] f[i][0/1] f[i][0/1]代表i的子树中是否使用边时最大深度最小的情况
同理 g [ i ] [ 0 / 1 ] g[i][0/1] g[i][0/1]是除掉i的子树…
线性dp即可

#include <queue>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define rep(i,l,r) for(int i=l;i<=r;++i)
#define per(i,l,r) for(int i=l;i>=r;--i)
using namespace std;

const int M=2e5+5;

int n;
int e_size,head[M];
int fa[M];
int f[M][2],g[M][2];
int ans[M];

struct edge {
    int v,w,nxt;
}e[M*2];

void e_add(int u,int v,int w) {
    e[++e_size]=(edge) {v,w,head[u]};
    head[u]=e_size;
}

void dp1(int x) {
    
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(v==fa[x]) continue;
        fa[v]=x;
        dp1(v);
    }


    f[x][0]=f[x][1]=0;
    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(v==fa[x]) continue;

        f[x][1]=min( min( max(f[x][1],f[v][0]+e[i].w) , max(f[x][0],f[v][1]+e[i].w) ) , max(f[x][0],f[v][0]) );
        f[x][0]=max(f[x][0],f[v][0]+e[i].w);
    }

    //cout<<"-_-"<<x<<" "<<f[x][0]<<" "<<f[x][1]<<endl;
}


void dp2(int x) {

    if(fa[x]) {
        g[x][0]=g[x][1]=0;

        g[x][0]=g[fa[x]][0];
        g[x][1]=g[fa[x]][1];

        int sum=0;

        for(int i=head[fa[x]];i;i=e[i].nxt) {
            int v=e[i].v;
            if(v==x) sum=e[i].w;
            if(v==fa[fa[x]]||v==x) continue;

            g[x][1]=min( min( max(g[x][1],f[v][0]+e[i].w) , max(g[x][0],f[v][1]+e[i].w) ) , max(g[x][0],f[v][0]) );
            g[x][0]=max(g[x][0],f[v][0]+e[i].w);
        }

        g[x][1]=min(g[x][1]+sum,g[x][0]);
        g[x][0]+=sum;
    }

    //cout<<x<<" "<<g[x][0]<<" "<<g[x][1]<<endl;


    for(int i=head[x];i;i=e[i].nxt) {
        int v=e[i].v;
        if(v==fa[x]) continue;

        dp2(v);
    }
}

int main() {
    //freopen("a.txt","r",stdin);

    int test_;
    cin>>test_;
    while (test_--) {

        scanf("%d",&n);
        rep(i,2,n) {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            e_add(x,y,z),e_add(y,x,z);
        }
        dp1(1);
        dp2(1);

        rep(i,1,n) f[i][1]=min(f[i][1],f[i][0]),g[i][1]=min(g[i][1],g[i][0]);

        rep(i,1,n) ans[i]=min( max(f[i][1],g[i][0]) , max(g[i][1],f[i][0]) );
        int p=1;
        rep(i,2,n) if(ans[i]<ans[p]) p=i;
        printf("%d %d\n",p,ans[p]);

        //rep(i,1,n) printf("%d ",ans[i]);
        //puts("");

        e_size=0;rep(i,1,n) head[i]=0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值