Codeforces每日一练周赛#1 C~G

C Rank of Tetris

来源:HDU 1811
并查集,拓扑排序
题意:N个人M条大小关系,问是否可以将这些人排序,如果同时存在不能确定和冲突的情况按冲突识别
很明显对于相等的我们可以把它合并在一起(因为相等的点我们一定可以内部排序),第一遍先遍历关系合并,第二遍遍历关系对于不等的点根据大小关系在他们的组长节点之间连一条单向边(刚开始写把相等也连了,然后de了半天),然后就拓扑排序并记录进队的节点数,存在环时,进队的节点数应该是小于联通块数地,而且如果某时刻队里有>1个元素,那么这几个元素的大小关系就无法确定。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);
#define maxn 10005
#define inf 1000000002
#define for_(i,n) for(int (i)=1;(i)<=(n);i++)
#define mod 80112002
struct node{
    int u,v;
    char ch;
};
node p[maxn*2];
int n,m,f[maxn],d[maxn],vis[maxn];
vector<int> g[maxn];
int findfa(int x){
    if(f[x]==x)return f[x];
    else f[x]=findfa(f[x]);
    return f[x];
}
int mergefa(int a,int b){
    int fa=findfa(a),fb=findfa(b);
    if(fa==fb)return 0;
    else f[fa]=fb;
    return 1;
}
int main(){
    while(cin>>n>>m){
        int fl=0,num=n;
        for(int i=0;i<n;i++){vis[i]=0;f[i]=i;d[i]=0;g[i].clear();}
        for_(i,m) {
            cin>>p[i].u;
            getchar();
            cin>>p[i].ch;
            cin>>p[i].v;
            if(p[i].ch=='='){if(mergefa(p[i].u,p[i].v))num--;}
        }
        int sig=0;
        for_(i,m) {
            char ch =p[i].ch;
            int u=findfa(p[i].u),v=findfa(p[i].v);
            if(ch=='=')continue;
            if(ch=='<'){g[v].push_back(u);d[u]++;}
            else{
                g[u].push_back(v);d[v]++;
            }
        }
     
        queue<int> q;
        for(int i=0;i<n;i++){
            if(d[i]==0&&f[i]==i){q.push(i);}
        }
        int cnt=0;
        while(!q.empty()){
            if(q.size()>1){
                fl=1;
            }
            int tmp=q.front();
            vis[tmp]=1;
            q.pop();
            cnt++;
            for(auto i:g[tmp]){
                d[i]--;
                if(d[i]==0){q.push(i);}
            }
        }
        if(cnt<num)cout<<"CONFLICT"<<endl;
        else {
            if(fl!=1)cout<<"OK"<<endl;
            else cout<<"UNCERTAIN"<<endl;}
    }
    return 0;
}

D Conscription

来源:POJ 3723
并查集,最小生成树
题意,每两个人之间有一条权值为w的边,当其中一个人被选择的时候另一个人的费用减少w,问最少花多少钱可以全部选择
明显是一道最大生成树,先按权值排序然后Kruscal求解即可,每次向树中加边时仅加上10000-w的费用(每个联通块可以看作是由一个节点开始不断选人组成的),最后根据联通块的个数决定有多少人是原价。
代码实现不难,注意CE即可

#include<iostream>
#include <cmath>
#include <string.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
typedef long long ll;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);
#define maxn 20005
#define inf 1000000002
#define for_(i,n) for(int (i)=1;(i)<=(n);i++)
#define mod 80112002
int gcd(int a,int b){if(b==0)return a;return gcd(b,a%b);}
int n,m,r;
int f[maxn];
int findfa(int x){
    if(f[x]==x)return x;
    else return f[x]=findfa(f[x]);
}
struct edge{
    int u,v,w;
};
int mergefa(int a,int b){
    int fa=findfa(a),fb=findfa(b);
    if(fa==fb)return 0;
    else f[fa]=fb;
    return 1;
}
bool cmp(edge a,edge b){
    return a.w>b.w;
}
edge e[50005];
int main(){
    int t;
    cin>>t;
    while(t--){
        scanf("%d %d %d",&n,&m,&r);
        for_(i,n+m)f[i-1]=i-1;
        int num=0;
        while(r--){
            scanf("%d %d %d",&e[num].u,&e[num].v,&e[num].w);
            e[num].v+=n;
            num++;
        }
        int ans=0,cnt=n+m;
        sort(e,e+num,cmp);
        for(int i=0;i<num;i++){
            if(mergefa(e[i].u,e[i].v)){
                ans+=10000-e[i].w;cnt--;
            }

        }
       printf("%d\n",ans+10000*cnt);
    }
    return 0;
}

E To Miss Our Children Time

来源:HDU 4001
线性dp
(第一遍缩点拓扑写WA了,尝试别的方法,然后发现就是简单的排序+dp
题意:三种类型的砖块,不同类型的砖块对其下方相邻的砖块要求不一,问最高能垒多高
按照x,y排个序就可以线性dp做了,输入的时候利用swap保证每个砖块的x,y大小关系一致(即比较时小对小,大对大),排序先排x,y,如果相等就看种类,显然种类id越大对下面相邻的砖块要求越苛刻,那么就尽量把这些砖块放下面就好

#include<bits/stdc++.h>
#define ios std::ios::sync_with_stdio(false)
using namespace std;
#define ll long long
int n;
#define maxn 1010
struct node{
    ll x,y,h,d;
};
ll dp[maxn];
node e[maxn];
bool cmp(node a,node b){
    if(a.x!=b.x)return a.x<b.x;
    if(a.y!=b.y)return a.y<b.y;
    return a.d>b.d;
}
signed main()
{
    ios ; cin.tie(0) , cout.tie(0);
    while(cin>>n){
        if(n==0)break;
        memset(dp,0, sizeof(dp)); ll ma=-1;
        for (int i = 0; i <n ; ++i) {
            cin>>e[i].x>>e[i].y>>e[i].h>>e[i].d;
            if(e[i].x>e[i].y){
                ll t=e[i].x;
                e[i].x=e[i].y;
                e[i].y=t;
            }
        }
        sort(e,e+n,cmp);
        ma=e[0].h;
        for (int j = 1; j <n ; ++j) {
            dp[j]=e[j].h;
            if(e[j].d==0){
                for (int i = 0; i <j ; ++i) {
                    if(e[i].x<=e[j].x&&e[i].y<=e[j].y){
                        dp[j]=max(dp[j],dp[i]+e[j].h);
                    }
                }
            }
            if(e[j].d==2){
                for (int i = 0; i <j ; ++i) {
                    if(e[i].x<e[j].x&&e[i].y<e[j].y){
                        dp[j]=max(dp[j],dp[i]+e[j].h);
                    }
                }
            }
            if(e[j].d==1){
                for (int i = 0; i <j ; ++i) {
                    if(e[i].x<=e[j].x&&e[i].y<=e[j].y&&e[i].x*e[i].y<e[j].x*e[j].y){
                        dp[j]=max(dp[j],dp[i]+e[j].h);
                    }
                }
            }
            ma=max(ma,dp[j]);
        }
        cout<<ma<<endl;
    }
    return 0;
}

G How many integers can you find

来源:HDU 1796
容斥+状压
题意:给你n,m和一个大小为m的集合,问你对所有的1<=i<n,有多少个i是集合里任意一个数的倍数
看到m的范围做法大致就出来了。
找了篇容斥原理的博客->传送门,这里就不多介绍了。
首先扔掉集合里为0的数,假设剩余cnt个,那么全选时的状态就可以表示为(1<<cnt)-1,最小的状态显然为1(只选第一个),那么只需要从1到(1<<cnt)-1遍历即可,如果某一位为1,就把该位对应的数字加进计算里,每个集合的结果显然就是(n-1)/当前所加入的数的最小公倍数,根据当前有几个数对答案操作即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define IOS ios_base::sync_with_stdio(false);cin.tie(0);
#define maxn 15
#define inf 1000000002
#define for_(i,n) for(int (i)=1;(i)<=(n);i++)
#define mod 80112002
int gcd(int a,int b){if(b==0)return a;return gcd(b,a%b);}
ll n;
int m;

int main(){
    while(cin>>n>>m){
        n--;
        int a[maxn];
        int cnt=1;
        for_(i,m){cin>>a[cnt];if(a[cnt]!=0)cnt++;}
        m=cnt-1;
        ll ans=0;
        for (int i = 1; i <(1<<m) ; ++i) {
            ll tmp=1,num=0;
            for_(j,m){
                if(i&(1<<(j-1))){
                    num++;
                    tmp=a[j]*tmp/gcd(tmp,a[j]);
                }
            }
            if(num&1)ans+=n/tmp;
            else ans-=n/tmp;
        }
        cout<<ans<<endl;
    }
    return 0;
}

F Activation

来源:HDU 4089
概率dp
传送门
(题意太长了,直接点吧
(如果p4<1e-5肯定不可能,直接输出0.00000
不难看出当前的概率只与队里多少人和主角的位置有关 不妨用dp[i][j]表示有i个人,主角在第i个时的概率,可以得到下面的关系式

  • dp[i][1]=dp[i][1]*p1+dp[i][i]*p2+p4
  • 2<=j<=k,dp[i][j]=dp[i][j-1]*p2+dp[i-1][j-1]*p3+dp[i][j]*p1+p4
  • k<j<=i,dp[i][j]=dp[i][j-1]*p2+dp[i-1][j-1]*p3+dp[i][j]*p1

然后可以发现知道了dp[i][i]就可以求出当前dp[i]的所有值了,而dp[i][i]可以根据第三个式子推出来,具体实现先看代码吧,我下了课再来补QAQ

#include<bits/stdc++.h>
using namespace std;
#define ios std::ios::sync_with_stdio(false)
#define ld double
#define eps 1e-5
const int maxn =2005;
ld dp[maxn][maxn],power[maxn];
signed main()
{
    int n,m,k;
    ld p1,p2,p3,p4,p21,p31,p41;
    while(scanf("%d %d %d %lf %lf %lf %lf", &n, &m, &k, &p1, &p2, &p3, &p4) != EOF){
        if(p4<eps){cout<<"0.00000"<<endl;continue;}
        p21=p2/(1-p1);
        p31=p3/(1-p1);
        p41=p4/(1-p1);
        power[0]=1.0;
        for(int i=1;i<=n;i++)power[i]=p21*power[i-1];
        dp[1][1]=p4/(1-p1-p2);
        for (int i = 2; i <=n ; ++i) {
            dp[i][i]=0;
            for(int j=1;j<=i;++j){
                if(j==1) dp[i][i]+=power[i-j]*p41;
                else if(j>=2&&j<=k) dp[i][i]+=power[i-j]*(p31*(dp[i-1][j-1])+p41);
                else dp[i][i]+=power[i-j]*p31*dp[i-1][j-1];
            }
            dp[i][i]/=(1-power[i]);
            for(int j=1;j<i;++j){
                if(j==1) dp[i][j]=dp[i][i]*p21+p41;
                else if(j>=2&&j<=k)dp[i][j]=dp[i][j-1]*p21+p41+dp[i-1][j-1]*p31;
                else  dp[i][j]=dp[i][j-1]*p21+dp[i-1][j-1]*p31;
            }
        }

        printf("%.5lf\n",dp[n][m]);
    }
    return 0;

发布了10 篇原创文章 · 获赞 10 · 访问量 3692

猜你喜欢

转载自blog.csdn.net/qq_43682148/article/details/105041108