NOI 2012

[NOI2012]随机数生成器

矩阵乘法,要注意两数相乘时,可能会爆long long,用快速乘。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
long long mod1,a,c,x,n,mod2;

long long mul(long long p,long long b)
{
    long long res=0;
    for(;b;b>>=1)
    {
        if(b&1) res=(res+p)%mod1;
        p=(p+p)%mod1;
    }
    return res;
}

struct Matrix
{
    long long a[10][10];
    Matrix(){memset(a,0,sizeof(a));}
    Matrix operator * (const Matrix &aa)
    {
        Matrix c;
        for(int i=1;i<=9;i++)
            for(int j=1;j<=9;j++)
                for(int p=1;p<=9;p++)
                    c.a[i][j]=(c.a[i][j]%mod1+mul((a[i][p]%mod1),(aa.a[p][j]%mod1))%mod1)%mod1;
        return c;
    }
};

Matrix Pow(Matrix p,long long b)
{
    Matrix res;
    for(int i=1;i<=3;i++) res.a[i][i]=1LL;
    for(;b;b>>=1)
    {
        if(b&1) res=res*p;
        p=p*p;
    }
    return res;
}

int main()
{
    scanf("%lld%lld%lld%lld%lld%lld",&mod1,&a,&c,&x,&n,&mod2);
    Matrix trans,ini;
    trans.a[1][1]=0,trans.a[1][2]=0,trans.a[1][3]=0;
    trans.a[2][1]=1,trans.a[2][2]=a%mod1,trans.a[2][3]=0;
    trans.a[3][1]=0,trans.a[3][2]=1,trans.a[3][3]=1;
    ini.a[1][1]=0,ini.a[1][2]=x,ini.a[1][3]=c%mod1;
    Matrix ans=ini*Pow(trans,n);
    printf("%lld",(ans.a[1][2]%mod2+mod2)%mod2);
    return 0;
}
View Code

[NOI2012]美食节

本来应该这样建图,把每个厨师拆成sigma_p个点,分别表示倒数第i次做菜的该位厨师j,向每个菜品p连边。

但是这样会TLE,我们不妨模拟一下MCMF的过程,对于每个厨师来说,首先SPFA出来的代价最小的通路肯定是最后一次做某菜,因为等待的时间短。我们不妨先建所有的倒数第一个点,哪个满流了就建倒数第二个点,很好的模拟了增广的过程,剪掉了许多不必要的边。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int maxm=1e7+10;
const int maxn=1e5+10;
#define idd(i,j) ((i-1)*sum+j+n)
#define left(x) ((x-n-1)/sum+1)
#define right(x) ((x-n-1)%sum+1)
struct point
{
    int to,nxt;
    int cap,cost;
}edge[maxm];
int n,m,Mincost,Maxflow,tot,S,T,sum;
int head[maxn],p[maxn],flow[maxn],vis[maxn],id[maxn],pre[maxn],dis[maxn];
int ti[120][120];

inline void add(int u,int v,int c,int f)
{
    edge[tot]=(point){v,head[u],c,f};
    head[u]=tot++;
    edge[tot]=(point){u,head[v],0,-f};
    head[v]=tot++;
}

inline bool Bfs()
{
    queue<int> q;
    memset(dis,-1,sizeof(dis));
    memset(vis,0,sizeof(vis));
    dis[S]=0; flow[S]=1e9; vis[S]=1;
    q.push(S);
    while(!q.empty())
    {
        int tt=q.front();
        q.pop();
        vis[tt]=0;
        for(int i=head[tt];~i;i=edge[i].nxt)    
        {
            int v=edge[i].to;
            if(edge[i].cap && (dis[v]==-1 || dis[v]>dis[tt]+edge[i].cost))
            {
                dis[v]=dis[tt]+edge[i].cost;
                flow[v]=min(edge[i].cap,flow[tt]);
                id[v]=i; pre[v]=tt;
                if(!vis[v]) vis[v]=1,q.push(v);
            }
        }
    }    
    return dis[T]!=-1;
}

inline void Mcmf()
{
    while(Bfs())
    {
        int tmp=T,ad=0;
        while(tmp!=S)
        {
            edge[id[tmp]].cap-=flow[T];
            edge[id[tmp]^1].cap+=flow[T];
            tmp=pre[tmp];
        }
        Mincost+=flow[T]*dis[T];
        ad=pre[T]+1;
        add(ad,T,1,0);
        for(int i=1;i<=n;i++)
            add(i,ad,1,ti[i][left(ad)]*right(ad));
    }
}

int main()
{
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&p[i]),sum+=p[i];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            scanf("%d",&ti[i][j]);
    S=0,T=sum*m+n+1;
    for(int i=1;i<=n;i++) add(S,i,p[i],0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            add(i,idd(j,1),1,ti[i][j]);
    for(int i=1;i<=m;i++)
        add(idd(i,1),T,1,0);
    Mcmf();
    printf("%d",Mincost);
    return 0;
}
View Code

[NOI2012]迷失游乐园

当只有树的时候很套路,up[x]表示从节点x向上走的期望长度,down[x]表示从节点x向下走的期望长度。

当是基环树的时候,先算down[x],每棵树的树根的up[x]要注意一下,左右全部跑一遍,对于每个环内的点都有可能继续走或下到树里面。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=1e5+10;
struct point
{
    int to;
    int nxt;
    double w;
}edge[maxn*2];
int n,m,tot,cnt,flag;
double up[maxn],down[maxn],w_fa[maxn],W[33][33];
int hav[maxn],nson[maxn],head[maxn],nn[33],in[maxn],vis[maxn],hash[maxn];
int fath[maxn],pre[maxn],nex[maxn];

inline void add(int u,int v,double w)
{
    tot++;
    edge[tot].nxt=head[u];
    edge[tot].to=v;
    edge[tot].w=w;
    head[u]=tot;
}

inline void dfs1(int x,int fa)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(vis[v] || v==fa) continue;
        nson[x]++,w_fa[v]=edge[i].w;
        hav[v]=1;
        dfs1(v,x);
        down[x]+=down[v]+(double)edge[i].w;
    }
    if(nson[x]) down[x]/=(double)nson[x];
}

inline void dfs2(int x,int fa)
{
    if(fa)
    {
        if(nson[fa]-1+hav[fa]>0) 
            up[x]=(double)(down[fa]*nson[fa]-w_fa[x]-down[x]+hav[fa]*up[fa])/(double)(nson[fa]-1+hav[fa]);
        up[x]+=(double)w_fa[x];
    }
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(vis[v] || v==fa) continue;
        dfs2(v,x);
    }
}

inline void Tree_solve()
{
    dfs1(1,0);
    dfs2(1,0);
}

inline void get_cir(int x,int fa)
{
    vis[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int v=edge[i].to;
        if(v==fa) continue;
        if(vis[v]){flag=v; return;}
        get_cir(v,x);
        if(flag>0){if(flag==x) flag=-1; return;}
        if(flag==-1) break;
    }
    vis[x]=0;
}

void find_cir(int x, int f) 
{
    if(hash[x]) return;
    nn[++cnt] = x;
    hash[x] = cnt;
    hav[x]=2;
    for(int i=head[x];i;i=edge[i].nxt) 
    {
        int v=edge[i].to;
        if (v == f) continue;
        if (!vis[v]) continue;
        pre[v] = x; nex[x] = v;
        find_cir(v, x);
        W[hash[x]][hash[v]] = W[hash[v]][hash[x]] = edge[i].w;
        break;
    }
}

inline void Cir_Tree_solve()
{
    for(int i=1;i<=n;i++) if(vis[i]) 
    {
        find_cir(i,0);
        break;    
    }
    double nowp;
    memset(w_fa,0,sizeof(w_fa));
    for(int i=1;i<=cnt;i++) dfs1(nn[i],0);
    for (int i=1;i<=cnt;i++)
    {
        double k=1.0;
        int u=nn[i];
        for(int j=nex[u];j!=u;j=nex[j])
        {
            if(nex[j]!=u) up[u]+=(double)k*((down[j]*nson[j])/(double)(nson[j]+1)+W[hash[pre[j]]][hash[j]]);
            else up[u]+=k*(down[j]+W[hash[pre[j]]][hash[j]]);
            k/=(nson[j]+1);
        }
        k=1.0;
        for(int j=pre[u];j!=u;j=pre[j])
        {
            if(pre[j]!=u) up[u]+=(double)k*((down[j]*nson[j])/(double)(nson[j]+1)+W[hash[nex[j]]][hash[j]]);
            else up[u]+=k*(down[j]+W[hash[nex[j]]][hash[j]]);
            k/=(nson[j]+1);
        }
        up[u]/=2.0;
    }
    for(int i=1;i<=cnt;i++) dfs2(nn[i],0);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        double w;
        scanf("%d%d%lf",&u,&v,&w);
        add(u,v,w),add(v,u,w);
    }
    get_cir(1,0);
    if(m<=n-1) Tree_solve();
    else Cir_Tree_solve();
    double ans=0.0;
    for(int i=1;i<=n;i++)
        ans=ans+(up[i]*hav[i]+down[i]*nson[i])/(double)(nson[i]+hav[i]);
    ans/=(double)n;
    printf("%.5lf",ans);
    return 0;
}
View Code

[NOI2012]骑行川藏

讲的很清楚。

CODE:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define eps 1e-12
const int maxn=1e4+10;
const int inf=1e9;
int n;
double E,s[maxn],k[maxn],v[maxn],x[maxn];

inline double cal(double lam)
{
    double tmp=0;
    for(int i=1;i<=n;i++)
    {
        double l=max(0.0,v[i]),r=inf;
        while(l+eps<r)
        {
            double mid=(l+r)*0.5;
            if(2*lam*k[i]*mid*mid*(mid-v[i])>1)
                r=mid;
            else l=mid;
        }
        x[i]=(l+r)*0.5;
        tmp+=k[i]*(x[i]-v[i])*(x[i]-v[i])*s[i];
    }
    return tmp;
}

int main()
{
    scanf("%d%lf",&n,&E);
    for(int i=1;i<=n;i++)
        scanf("%lf%lf%lf",&s[i],&k[i],&v[i]);
    double l=0,r=inf;
    while(l+eps<r)
    {
        double mid=(l+r)*0.5;
        if(cal(mid)>=E) l=mid;
        else r=mid;
    }
    double ans=0;
    for(int i=1;i<=n;i++)
        ans+=s[i]/x[i];
    printf("%.10lf",ans);
    return 0;
}
View Code

 

猜你喜欢

转载自www.cnblogs.com/linda-fcj/p/9131599.html