zoukankan      html  css  js  c++  java
  • [3.17校内训练赛]

    hzwer出的bzoj训练赛。

    A.[bzoj1823][jsoi2010]满汉全席

    有n种食材,每道食材可以做出两道菜。然后有m为评审,每位评审只对两道菜满意。你有恰好这n种食材各一个,你要决定每种食材做哪种菜,判断是否有一种做法满足所有评审。n<=100,m<=1000

    题解:2-sat裸题,对于每个评审满意的两道菜i,j,从i'向j,从j'向i连边,表示选了i的另一道菜必须选第j道菜,另一个同理。

    然后按照2-sat那么搞呗。

    #include<iostream>
    #include<cstdio>
    #define MAXN 200
    #define INF 2000000000
    #include<queue> 
    #include<cstring>
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    bool inq[MAXN+5];
    int head[MAXN+5],cnt,n,m,q[MAXN+5],cft[MAXN+5],top,dn;
    int dfn[MAXN+5],low[MAXN+5],cc,belong[MAXN+5],in[MAXN+5],col[MAXN+5];
    struct edge{
        int to,next;
    }e[1000000];
    char st[1000];
    
    int get()
    {
        scanf("%s",st+1);int x=0;
        for(int i=2;st[i];i++)
            x=x*10+st[i]-'0';
        if(st[1]=='m')x+=n;
        return x;
    }
    
    void ins(int f,int t)
    {
        e[++cnt].next=head[f];head[f]=cnt;
        e[cnt].to=t;
    }
    
    void tarjan(int x)
    {
        q[++top]=x;dfn[x]=low[x]=++dn;inq[x]=1;
        for(int i=head[x];i;i=e[i].next)
        if(!dfn[e[i].to]){tarjan(e[i].to);low[x]=min(low[x],low[e[i].to]);}
        else if(inq[e[i].to])low[x]=min(low[x],dfn[e[i].to]);
        if(dfn[x]==low[x])
        for(++cc;q[top+1]!=x;inq[q[top]]=0,belong[q[top--]]=cc);
    }
    
    int main()
    {
        int t=read();
        while(t--)
        {
            cc=n=read();m=read();cnt=top=0;memset(head,0,sizeof(head));dn=0;
            memset(belong,0,sizeof(belong));memset(dfn,0,sizeof(dfn));memset(low,0,sizeof(low));
            for(int i=1;i<=n;i++)cft[i]=i+n,cft[i+n]=i;
            for(int i=1;i<=m;i++)
            {
                int x=get(),y=get();
                ins(cft[x],y);ins(cft[y],x);    
            }
            for(int i=1;i<=n<<1;i++)if(!dfn[i])tarjan(i);
            int i;
            for(i=1;i<=n;i++)if(belong[i]==belong[i+n]){puts("BAD");break;}
            if(i>n)puts("GOOD");
        }
        return 0;
    }

    B.[bzoj1822][jsoi2010]Frozen Nova冷冻波

    有n个巫师,每个巫师有坐标,攻击范围和冷却时间。有q棵树,每棵树抽象成圆,给定坐标和半径。还有m个小精灵,给定坐标。

    对于一个巫师和一个小精灵,如果小精灵在巫师的攻击距离内而且它们的连线不与任何的树相交,那么巫师可以在一次攻击中消灭这个小精灵。

    一个巫师在攻击后必须等待冷却时间结束才能发动下一次攻击。求最少过了多久可以消灭所有小精灵,或者根本无法杀掉所有小精灵?

    n,m,k<=200

    题解:很容易想到二分答案+网络流check,所以剩下的就是计算几何啦。

    作为一名计算几何菜鸡,我一下午就wa了个几十次吧,最后发现是sb错误。真的难受。

    #include<iostream>
    #include<cstdio>
    #define INF 2000000000
    #include<queue> 
    #include<cmath>
    #include<cstring>
    #define S 0
    #define T 401
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
      
    bool mp[T+5][T+5];
    int head[T+5],n,m,cnt=1,k,q[T+5];
    struct edge{
        int to,next,w;
    }e[1000000];
    inline double sqr(double x){return x*x;}
    struct POINT{double x,y,r,t;
        POINT operator-(POINT a){return (POINT){a.x-x,a.y-y};}
        double operator*(POINT a){return x*a.y-y*a.x;}
    }s[T],t[T],w[T];
    double dis(POINT x,POINT y){return sqrt(sqr(x.x-y.x)+sqr(x.y-y.y));}
    double dot(POINT x,POINT y){return x.x*y.x+x.y*y.y;}
     
    queue<int> qu;
    bool used[T+5];
      
    void ins(int f,int t,int w)
    {
        e[++cnt].next=head[f];head[f]=cnt;
        e[cnt].to=t;e[cnt].w=w;
    }
      
    void insw(int f,int t,int w){ins(f,t,w);ins(t,f,0);}
      
    int dfs(int x,int f)
    {
        if(x==T)return f;
        int use=0;
        for(int i=head[x];i;i=e[i].next)
        if(e[i].w&&q[e[i].to]==q[x]+1)
        {
            int w=dfs(e[i].to,min(f-use,e[i].w));
            use+=w;e[i].w-=w;e[i^1].w+=w;
            if(use==f)return f;
        }
        return use;
    }
      
    bool bfs()
    {
        memset(q,0,sizeof(q));q[S]=1;qu.push(S);
        while(!qu.empty())
        {
            int u=qu.front();qu.pop();
            for(int i=head[u];i;i=e[i].next)    
            if(e[i].w&&!q[e[i].to])
            {
                q[e[i].to]=q[u]+1;
                qu.push(e[i].to);   
            }
        }
        return q[T]>0;
    }
      
    bool check(POINT x,POINT y,POINT z)
    {
        double d=fabs((z-x)*(y-x)),len=dis(x,y);
        if((double)d/(double)len>z.r) return true;
        if(dot(y-x,z-x)<0)return dis(x,z)>z.r;
        else if(dot((x-y),(z-y))<0)return dis(y,z)>z.r;
        return false;
    }
      
    void build(int x)
    {
        cnt=1;memset(head,0,sizeof(head));
        for(int i=1;i<=n;i++)insw(S,i,x/w[i].t+1);
        for(int j=1;j<=m;j++)insw(j+n,T,1);
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                if(mp[i][j])
                insw(i,j+n,1);
    }
      
    int solve()
    {
        int l,r,mid,ans=-1,i,j,sum;l=0,r=200000000;
        while(l<=r)
        {
            mid=l+r>>1;sum=0;build(mid);
            while(bfs())sum+=dfs(S,INF);
            //cout<<mid<<" "<<sum<<"GGF"<<endl;
            if(sum==m)ans=mid,r=mid-1;
            else l=mid+1;
        }
        return ans;
    }
      
    int main()
    {
        n=read();m=read();k=read();
        for(int i=1;i<=n;i++)
        {w[i].x=read();w[i].y=read();w[i].r=read();w[i].t=read();}
        for(int i=1;i<=m;i++)
        {s[i].x=read();s[i].y=read();}
        for(int i=1;i<=k;i++)
        {t[i].x=read();t[i].y=read();t[i].r=read();}
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
            if(dis(w[i],s[j])<=w[i].r)
            {
                bool yes=true;
                for(int l=1;l<=k;l++)
                {
                    if(!check(w[i],s[j],t[l])){yes=false;break;}
                }
                if(yes)mp[i][j]=1; 
            }
        cout<<solve();
        return 0;
    }

    C.[bzoj4008][hnoi2015]亚瑟王

    给定n张卡片,每张卡片有一个发动概率pi和伤害d。你进行r轮游戏,每次都从第一张卡片开始,每次对于一张没发动过的卡片,你都有pi的几率发动它并结束这一轮。当然,你也可能什么都不发动。求最后伤害的期望值。

    我根本不会,求助ditoly大佬。

    ditoly大佬的题解:我们可以把问题抽象为一开始我有r条线位于点1,我可以选择删掉其中的任何一条线,使得其他线都往后走一格(就是这一轮发动它并停止了),或者不删掉,直接走,两者的概率不同。

    先用r[i][j]表示有i条线走到了j点,选的可能性。则r[i][j]可以是之前已经选了,或者恰好选最后这个。r[i][j]=r[i-1][j]+(1-r[i-1][j])*p[i]

    再用r2[i][j]表示从起始状态走到这个状态的可能性,则r2[i][j]可以是从r2[i+1][j-1]转移过来的,这时选了第i个,概率是r[i+1][j];或者也可以从r2[i][j-1]转移过来,相应的概率是(1-r[i][j])

    最后计算期望。相似的,f[i][j]可以从f[i+1][j-1]和f[i][j-1]转移,f[i][j]=(f[i+1][j-1]+r2[i+1][j-1]*s[j])*r[i+1][j]+f[i][j-1]*r[i][j]。所以这道题就解完了。

    然后再说一下网上的比较简单的题解:用f[i][j]表示第i个人得到第j个机会的概率,那么这时候f[i,j]=f[i1,j]*(1pi1)^j+f[i1,j+1]*(1(1pi1)^j+1)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
     
    double q[225];
    double s[225];
    double f[135][225],r[135][225],r2[135][225];
    int n,m;
    double ans=0;
     
    int main()
    { 
        int T=read();
        while(T--)
        {
            n=read();m=read();memset(f,0,sizeof(f));ans=0;
            memset(r,0,sizeof(r));memset(r2,0,sizeof(r2));
            for(int i=1;i<=n;i++)scanf("%lf",&q[i]),s[i]=read();
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                    r[j][i]=r[j-1][i]+(1-r[j-1][i])*q[i];
            r2[m][0]=1;
            for(int i=m;i>=0;i--)
                for(int j=1;j<=n;j++)
                    r2[i][j]=r2[i+1][j-1]*r[i+1][j]+r2[i][j-1]*(1-r[i][j]);
            for(int i=m;i>=0;i--)
                for(int j=1;j<=n;j++)
                    f[i][j]=f[i+1][j-1]*r[i+1][j]+s[j]*r2[i+1][j-1]*r[i+1][j]+f[i][j-1]*(1-r[i][j]);
            for(int i=0;i<=m;i++)ans+=f[i][n];
            printf("%0.10lf
    ",ans);
        }    
        return 0;
    }

    D.[bzoj3144][hnoi2013]切糕

    有n*m个格子,每个格子可以填1-h中的任意数,每个格子填每个数都有不同的不满意度。你要在相邻的两个格子填的数字之差不超过d的情况下,求出最小的不满意度。n,m,h<=40

    题解:最小割。

    对于每个格子,拆h个点,(i,j,k-1)向(i,j,k)连边,流量为Sijk,每个(i,j,h)向T连费用为INF的边。

    然后对于每个点(i,j,k)且k>d,从它向四周的点的(k-d)号点连INF的边,使其无法被割。

    然后这样可以保证正确性。如果割掉了连接(i,j,k)到(i,j,k+1)的边,那么它就会顺着边流到(i',j',k-d),之后在那一竖又回流回到(i,j)这一竖。

    复杂度O(能过)

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<queue>
    #define S 0
    #define T 64001
    #define INF 2000000000
    using namespace std;
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    
    int qu[T+5],top,tail;
    int n,m,h,d,cnt=1;
    int head[T+5],q[T+5],cur[T+5];
    const int dis[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
    struct edge{
        int to,next,w;
    }e[T*100];
    
    inline int num(int x,int y,int z){return z==0?S:(z-1)*n*m+(x-1)*m+y;}
    inline void ins(int f,int t,int w){e[++cnt].next=head[f];head[f]=cnt;e[cnt].to=t;e[cnt].w=w;}
    inline void insw(int f,int t,int w){ins(f,t,w);ins(t,f,0);}
    
    int dfs(int x,int f)
    {
        if(x==T)return f;
        int used=0;
        for(int i=cur[x];i;i=e[i].next)
        if(e[i].w&&q[e[i].to]==q[x]+1)
        {
            int w=dfs(e[i].to,min(e[i].w,f-used));
            used+=w;e[i].w-=w;e[i^1].w+=w;
            if(e[i].w)cur[x]=i;
            if(used==f)return used;
        }
        if(!used)q[x]=-1;
        return used;
    }
    
    bool bfs()
    {
        memset(q,0,sizeof(q));q[S]=1;qu[top=1]=S;tail=0;
        while(top!=tail)
        {
            int u=qu[++tail];
            for(int i=head[u];i;i=e[i].next)
            if(e[i].w&&!q[e[i].to])
            {
                q[e[i].to]=q[u]+1;
                qu[++top]=e[i].to;
            }
        }
        return q[T]>0;
    }
    
    int main()
    {
        n=read();m=read();h=read();d=read();
        for(int k=1;k<=h;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=m;j++)
                {int x=read();insw(num(i,j,k-1),num(i,j,k),x);}
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)insw(num(i,j,h),T,INF);
        for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int k=d+1;k<=h;k++)
        for(int l=0;l<4;l++)
        {
            int xx=i+dis[l][0],yy=j+dis[l][1];
            if(xx<1||yy<1||xx>n||yy>m)continue;
            insw(num(i,j,k),num(xx,yy,k-d),INF);
        }
        int ans=0;
        while(bfs())
        {for(int i=S;i<=T;i++)cur[i]=head[i];ans+=dfs(S,INF);}
        printf("%d
    ",ans);
        return 0;
    }

    E.[bzoj3573][hnoi2014]米特运输

    题意:给定一棵树,每个点有权值,你要修改尽量少的点使得:1)每个点的权值=它的子节点的权值和2)它的子节点的权值相同 n<=500000

    题解:确定一个值以后,整棵树都确定了。我们设根是x,则每个点都可以表示为x的若干分之一。我们只要把每个点的权值*这个若干就能得到对应的x,求出现次数最多的x就可以了。但是x可能非常大,所以我们把它取余一下,然后扔到map里就可以啦

    复杂度nlogn

    #include<iostream>
    #include<cstdio>
    #include<queue> 
    #include<cstring>
    #include<map>
    #define MAXN 500000
    #define mod 998244353
    #define ll long long
    using namespace std;
    inline int read()
    {
        int  x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
     
    struct edge{
        int to,next;
    }e[2*MAXN+5];
    int n,s[MAXN+5],num[MAXN+5],head[MAXN+5],f[MAXN+5],cnt=0;
    map<ll,int> mp;
     
    void ins(int f,int t){e[++cnt].next=head[f];head[f]=cnt;e[cnt].to=t;}
     
    void dfs(int x,int fa)
    {
        f[x]=fa;
        for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa)dfs(e[i].to,x),num[x]++;
    }
     
    void solve(int x,ll nn,int fa)
    {
        mp[(1LL*nn*s[x])%mod]++;
        for(int i=head[x];i;i=e[i].next)if(e[i].to!=fa)
        solve(e[i].to,nn*num[x]%mod,x);
    }
     
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++)s[i]=read();
        for(int i=1;i<n;i++)
        {int u=read(),v=read();ins(u,v);ins(v,u);}dfs(1,0);
        solve(1,1,0);int ans=0;
        for(map<ll,int>::iterator it=mp.begin();it!=mp.end();++it)
            ans=max(ans,it->second);
        cout<<n-ans;
        return 0;
    }
  • 相关阅读:
    程序调试的利器GDB
    Building a Android Development Environment
    手机的串号IMEI/ESN标示位置图解摩托罗拉官方教程
    Linux调试信息输出串口设备号的设置
    git忽略文件提交
    Spring @Transactional事务传播范围以及隔离级别
    自定义springbootstarter
    maven 命令将jar包安装到本地仓库
    oracle 常用命令
    Oracle 用户(user)和模式(schema)的区别
  • 原文地址:https://www.cnblogs.com/FallDream/p/hzwer317.html
Copyright © 2011-2022 走看看