1. 程式人生 > >hdu多校第十場題解(>=100人)

hdu多校第十場題解(>=100人)

時間不多了,然而hdu的多校還沒開始補。。博主打算先解決掉>=100人的題,其他的題等以後再補吧。
Problem E. TeaTree
第一次見線段樹合併的題,沒想到線段樹還能這麼用。真好玩。。
這題沒什麼邏輯問題,std也寫的很好。這裡就不多說了。

#include<bits/stdc++.h>
using namespace std;
const int maxv=1e5+5;
const int maxn=1e5+5;
const int maxnd=8.1e7+10;
vector<int>V[maxv];
void init()
{
    for
(int i=1;i<maxv;i++) for(int j=1;j*i<maxv;j++) V[j*i].push_back(i); } int root[maxn]; int tot; int ch[maxnd][2]; int newnode() { tot++; ch[tot][0]=ch[tot][1]=0; return tot; } void build(int l,int r,int v,int &rt) { if(rt==0)rt=newnode(); if(l==r)return; int
m=l+r>>1; if(v<=m)build(l,m,v,ch[rt][0]); else build(m+1,r,v,ch[rt][1]); } int f; int ans[maxn]; int fa[maxn]; int merge(int root1,int root2,int l,int r) { if(root1==0||root2==0) { return root1^root2; } if(l==r) { ans[f]=max(ans[f],l); return
root1; } int m=l+r>>1; ch[root1][0]=merge(ch[root1][0],ch[root2][0],l,m); ch[root1][1]=merge(ch[root1][1],ch[root2][1],m+1,r); return root1; } int main() { init(); memset(ans,-1,sizeof(ans)); int n; scanf("%d",&n); for(int i=2;i<=n;i++) scanf("%d",&fa[i]); int x; for(int i=1;i<=n;i++) { scanf("%d",&x); root[i]=newnode(); for(int j=0;j<V[x].size();j++) build(1,maxv,V[x][j],root[i]); } for(int i=n;i>=1;i--) { f=fa[i]; root[f]=merge(root[f],root[i],1,maxv); } for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }

Problem G. Cyclic
比賽的時候oeis過的,我覺得這樣不行。。
看了題解才覺得自己推公式才最爽。題解也寫的很詳細了。

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int maxn=1e5+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<=100000;i++)
        tab1[i]=tab1[i-1]*i%mod;
    tab2[100000]=mod_reverse(tab1[100000],mod);
    for(int i=99999;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;
}
int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        long long ans=0;
        for(int i=0;i<n;i++)
        {
            int flag=(i%2?-1:1);
            ans=(ans+1LL*flag*C(n,i)*tab1[n-1-i]%mod)%mod;
        }
        if(n%2)ans--;
        else ans++;
        printf("%lld\n",(ans%mod+mod)%mod);
    }



}

Problem I. Count
這個題也是oeis過的嗚嗚嗚。。
題解已經將式子化成這樣
i=1na=1i1[gcd(2ia,a)=1]
為什麼會往下面化呢,這裡說明兩點內容。
1,x如果與n互質->n-x與n互質
2,n是奇數->與n互質的奇數佔一半

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e7+5;

int phi[maxn];
void phi_table(int n)
{
    for(int i=2;i<=n;i++)phi[i]=0;
    phi[1]=1;
    for(int i=2;i<=n;i++)if(!phi[i])
        for(int j=i;j<=n;j+=i)
    {
        if(!phi[j])phi[j]=j;
        phi[j]=phi[j]/i*(i-1);
    }
}
long long sum[maxn];
void init()
{
    phi_table(maxn-5);
    for(int i=2;i<=maxn-5;i++)
    {
        sum[i]=sum[i-1];
        if(i%2)
            sum[i]+=phi[i]/2;
        else sum[i]+=phi[i];
    }
}

int main()
{
    init();
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n;
        scanf("%d",&n);
        printf("%lld\n",sum[n]);
    }
    return 0;



}

Problem J. CSGO
這題好巧妙啊。。
由於k不大,後面的累加實際上可以用(1<

#include<bits/stdc++.h>
using namespace std;

long long S[1<<5],B[1<<5];
const long long inf=0x3f3f3f3f3f;
int x[6];
int main()
{

    int T;
    scanf("%d",&T);

    while(T--)
    {
        memset(S,-inf,sizeof(S));
        memset(B,-inf,sizeof(B));
        int n,m,K;
        scanf("%d %d %d",&n,&m,&K);
        for(int i=1;i<=n;i++)
        {
            int val;
            scanf("%d",&val);
            for(int j=0;j<K;j++)
                scanf("%d",&x[j]);
            for(int j=0;j<(1<<K);j++)
            {
                long long res=0;
                for(int k=0;k<K;k++)
                    res+=((((j>>k)&1)<<1)-1)*x[k];
                res+=val;
                S[j]=max(S[j],res);
            }
        }
        for(int i=1;i<=m;i++)
        {
            int val;
            scanf("%d",&val);
            for(int j=0;j<K;j++)
                scanf("%d",&x[j]);
            for(int j=0;j<(1<<K);j++)
            {
                long long res=0;
                for(int k=0;k<K;k++)
                    res+=((((j>>k)&1)<<1)-1)*x[k];
                res+=val;
                B[j]=max(B[j],res);
            }
        }
        long long ans=-inf;
        for(int i=0;i<(1<<K);i++)
            ans=max(ans,S[i]+B[(1<<K)-1-i]);
        printf("%lld\n",ans);
    }
    return 0;

}

Problem L.Videos
這個費用流建圖方式也好棒啊。。
題解講的很詳細了。

#include<bits/stdc++.h>
using namespace std;
const int maxn1=205;
//下標從0到N-1
//最小費用最大流,求最大費用只需取相反數,結果取相反數即可。
const int maxn=800;
const int maxm=maxn*maxn;
const int inf=0x3f3f3f3f;
struct Edge
{
    int to,next,cap,flow,cost;
    Edge(int _t,int _n,int _c,int _f,int _cost):to(_t),next(_n),cap(_c),flow(_f),cost(_cost){}
    Edge(){}
}edges[maxm];
int head[maxn],tot;
int pre[maxn],dis[maxn];
bool vis[maxn];
int N;
void init(int n)
{
    N=n;
    tot=0;
    memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int cap,int cost)
{
    edges[tot]=Edge(v,head[u],cap,0,cost);
    head[u]=tot++;
    edges[tot]=Edge(u,head[v],0,0,-cost);
    head[v]=tot++;
}
bool spfa(int s,int t)
{
    queue<int>q;
    for(int i=0;i<N;i++)
    {
        dis[i]=inf;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[s]=0;
    vis[s]=true;
    q.push(s);
    while(!q.empty())
    {
        int u=q.front();q.pop();
        vis[u]=false;
        for(int i=head[u];~i;i=edges[i].next)
        {
            int v=edges[i].to;
            if(edges[i].cap>edges[i].flow&&dis[v]>dis[u]+edges[i].cost)
            {
                dis[v]=dis[u]+edges[i].cost;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t]==-1)return 0;
    return 1;
}
int minCostMaxflow(int s,int t,int &cost)
{
    int flow=0;
    cost=0;
    while(spfa(s,t))
    {
        int Min=inf;
        for(int i=pre[t];~i;i=pre[edges[i^1].to])
        {
            if(Min>edges[i].cap-edges[i].flow)
                Min=edges[i].cap-edges[i].flow;
        }
        for(int i=pre[t];~i;i=pre[edges[i^1].to])
        {
            edges[i].flow+=Min;
            edges[i^1].flow-=Min;
            cost+=edges[i].cost*Min;
        }
        flow+=Min;
    }
    return flow;
}

int id[maxn1][2];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m,k,W;
        scanf("%d %d %d %d",&n,&m,&k,&W);
        int tot1=0;
        int S=++tot1,T=++tot1;
        id[0][0]=id[0][1]=++tot1;
        for(int i=1;i<=n;i++)
        {
            id[i][0]=++tot1;
            id[i][1]=++tot1;
        }
        init(maxn);
        addedge(S,id[0][0],k,0);
        id[n+1][0]=id[n+1][1]=T;
        for(int i=0;i<=n;i++)
        {
            addedge(id[i][0],id[i+1][0],inf,0);
            addedge(id[i][1],id[i+1][1],inf,0);
        }
        int st,ed,w,op;
        for(int i=1;i<=m;i++)
        {
            scanf("%d %d %d %d",&st,&ed,&w,&op);
            ++tot1;
            addedge(id[st][op],tot1,1,0);
            addedge(tot1,id[ed][!op],1,-w);
            addedge(tot1,id[ed][op],1,W-w);
        }
        int ct;
        minCostMaxflow(S,T,ct);
        printf("%d\n",-ct);
    }
    return 0;
}