Winter Training #1

A
http://codeforces.com/problemset/problem/229/A
题意:给100个长1e4的01串,问最少的移动次数,造出一列全是1的
做法:我是把所有1找出来,如果有一行没有1显然gg。然后因为循环,所以在前面和后面各补一个。然后枚举1列的位置,对每行二分取到最小值求和即可。
代码:

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

char str[105][10500];

int cnt[105];
int rec[105][10500];

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=n;i++)scanf("%s", str[i]+1);
    bool ok=true;
    for(int i=1;i<=n;i++){
        bool flag=false;
        for(int j=1;j<=m;j++)if(str[i][j]=='1')flag=true;
        if(!flag){
            ok=false;
            break;
        }
    }
    if(!ok){
        printf("-1\n");
        return 0;
    }
    memset(cnt, 0, sizeof(cnt));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)if(str[i][j]=='1')rec[i][++cnt[i]]=j;
        rec[i][0]=rec[i][cnt[i]]-m;
        rec[i][cnt[i]+1]=rec[i][1]+m;
    }
    int M=105*10500;
/*  for(int i=1;i<=n;i++){
        for(int j=0;j<=cnt[i]+1;j++)printf("%d ", rec[i][j]);
        printf("\n");
    }*/
    for(int i=1;i<=m;i++){
        int tmp=0;
        for(int j=1;j<=n;j++){
            tmp+=min(i-*(upper_bound(rec[j], rec[j]+cnt[j]+2, i)-1), *(lower_bound(rec[j], rec[j]+cnt[j]+1, i))-i);
        }
        M=min(M, tmp);
    }
    printf("%d\n", M);
}

B
http://codeforces.com/problemset/problem/229/B
题意:简单的最短路,附加条件是每个点有些时间不能走。
做法:在dij里改一下就行了。我一开始改了T了,后来发现因为会一直重复访问不能走的一段,加了一个d1数组防止重复访问就过了。
代码:

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

struct edge{
    int to, cost;
};

typedef pair<int, int> P;

int n;
vector<edge>g[105000];
int d[105000];
int d1[105000];
vector<int>rec[105000];

void dij(int s){
    priority_queue<P, vector<P>, greater<P> >que;
    for(int i=1;i<=n;i++)d[i]=d1[i]=1e9+1e8;
    int st=0;
    for(vector<int>::iterator it=rec[s].begin();it!=rec[s].end();it++){
        if(*it<st)continue;
        else if(*it==st)st++;
        else break;
    }
    d[s]=d1[s]=st;
    que.push(P(st, s));
    while(!que.empty()){
        P p=que.top();que.pop();
        int v=p.second;
        if(d[v]<p.first)continue;
        for(vector<edge>::iterator it=g[v].begin();it!=g[v].end();it++){
            edge e=*it;
            if(d1[e.to]>d[v]+e.cost){
                d1[e.to]=d[e.to]=d[v]+e.cost;
                if(e.to==n)break;
                for(vector<int>::iterator it1=rec[e.to].begin();it1!=rec[e.to].end();it1++){
                    if(*it1<d[e.to])continue;
                    else if(*it1==d[e.to])d[e.to]++;
                    else break;
                }
                que.push(P(d[e.to], e.to));
            }
        }
    }
}

int main()
{
    int m;
    scanf("%d%d", &n, &m);
    for(int i=1;i<=m;i++){
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        edge t1, t2;
        t1.to=b, t1.cost=c;
        g[a].push_back(t1);
        t2.to=a, t2.cost=c;
        g[b].push_back(t2);
    }
    for(int i=1;i<=n;i++){
        int num;
        scanf("%d", &num);
        for(int j=1;j<=num;j++)
        {
            int tmp;
            scanf("%d", &tmp);
            rec[i].push_back(tmp);
        }
    }
    dij(1);
    if(d[n]==1e9+1e8)printf("-1\n");
    else printf("%d\n", d[n]);
}

C
http://codeforces.com/contest/229/problem/C
题意:将一个1e6个点的完全图取1e6条边的子图,求这个子图和它的反图里一共有多少个三角形。
做法:考虑完全图的三角形数,是C(n,3);对于集合A、B,C(n,3)=由同1个集合的边构成的三角形+由A、B共同构成的三角形。所以我们只需要用C(n,3)减去三边分属于A、B集合的三角形即可。在一个n完全图中,一次拿掉一条边,与这条边有关的三角形有n-2个,减去n-2。但如果这条边的两端点已经有边,则减去n-2就多减了,作为补偿,要加上这两个点已有的边数,因为这些边和当前边确定的三角形已经在之前被减过了、
代码:

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

typedef long long ll;

ll rec[1050000];

int main()
{
    memset(rec, 0, sizeof(rec));
    ll n, m;
    scanf("%I64d%I64d", &n, &m);
    if(n<=2){
        printf("0\n");
        return 0;
    }
    ll ans=n*(n-1)*(n-2)/6;
    memset(rec, 0, sizeof(rec));
    for(int i=1;i<=m;i++){
        int x, y;
        scanf("%d%d", &x, &y);
        ans-=(n-2-rec[x]-rec[y]);
        rec[x]++;
        rec[y]++;
    }
    printf("%I64d\n", ans);
}

因为po主太菜了,D和E不会,待赛后填坑吧。

E
http://codeforces.com/contest/229/problem/E
题意:略
做法:看了xhr大爷的题解后发现,我特么题少看了个条件就是同名的价格两两不同,之后的事情就是正常的概率dp了。注意dp存的是前i种物品取了j个边界值的所有情况的成功率之和,所以最后还要除掉所有情况的种类数。
代码:

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

int n, m;
int pre[1050];
int rec[1050][1050];
int tot;
int cnt[1050];
int upp[1050];
bool exs[1050];
int tag;
double c[1050][1050];
double dp[1050][1050];

int main()
{
    scanf("%d%d", &n, &m);
    tot=0;
    for(int i=1;i<=m;i++){
        scanf("%d", &cnt[i]);
        for(int j=1;j<=cnt[i];j++){
            scanf("%d", &rec[i][j]);
            pre[++tot]=rec[i][j];
        }
    }
    sort(pre+1, pre+1+tot);
    tag=pre[tot-n+1];
    memset(exs, false, sizeof(exs));
    memset(upp, 0, sizeof(upp));
    for(int i=1;i<=m;i++){
        for(int j=1;j<=cnt[i];j++){
            if(rec[i][j]==tag)exs[i]=true;
            else if(rec[i][j]>tag)upp[i]++;
        }
    }
    for(int i=1;i<=1005;i++)
        for(int j=0;j<=i;j++){
            if(j==0||j==i)c[i][j]=1;
            else c[i][j]=c[i-1][j-1]+c[i-1][j];
        }
    int eql=0;
    int supp=0;
    for(int i=1;i<=m;i++)if(exs[i])eql++;
    for(int i=1;i<=m;i++)supp+=upp[i];
    for(int i=0;i<=1005;i++)for(int j=0;j<=1005;j++)dp[i][j]=0;
    dp[0][0]=1;
    for(int i=1;i<=m;i++)
        for(int j=0;j<=n-supp;j++)
        {
            if(exs[i]&&j){
                dp[i][j]=dp[i-1][j]/c[cnt[i]][upp[i]]+dp[i-1][j-1]/c[cnt[i]][upp[i]+1];
            }
            else {
                dp[i][j]=dp[i-1][j]/c[cnt[i]][upp[i]];
            }
        }
    printf("%.11f\n", dp[m][n-supp]/c[eql][n-supp]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值