Codeforces Round #343 (Div. 2)

链接:http://codeforces.com/contest/629

problemA:给你一个n*n的矩阵,矩阵每个格子要么为空要么为C,求有多少对满足条件的C,条件为这两个C在同一行或同一列。

分析:直接统计每一行和每一列的C的个数,然后k*(k-1)/2加入答案即可。O(n^2)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=410;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
char s[110];
int l[110],h[110];
int main()
{
    int i,j,n,ans=0;
    scanf("%d", &n);
    memset(l,0,sizeof(l));
    memset(h,0,sizeof(h));
    for (i=1;i<=n;i++) {
        scanf("%s", s);
        for (j=0;j<n;j++)
        if (s[j]=='C') { l[i]++;h[j+1]++; }
    }
    for (i=1;i<=n;i++) ans+=l[i]*(l[i]-1)/2;
    for (i=1;i<=n;i++) ans+=h[i]*(h[i]-1)/2;
    printf("%d\n", ans);
    return 0;
}


problemB:给你n个人的信息,每个人的信息包含性别和他能去生日派对的时间区间[l,r],问在哪一天能邀请到最多的人数,并且男女人数相等,输出这个最大人数。

分析:因为日期的范围为1~366,所以暴力统计每一天男女的人数,然后找出最大的人数即可,这样做是O(366*n)的,用差分序列可以做到O(n),不过在比赛的时候也懒得处理了。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=410;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
char s[5];
int m[400],f[400];
int main()
{
    int i,j,n,l,r,ans=0;
    scanf("%d", &n);
    memset(m,0,sizeof(m));
    memset(f,0,sizeof(f));
    for (i=1;i<=n;i++) {
        scanf("%s%d%d", s, &l, &r);
        if (s[0]=='M') {
            for (j=l;j<=r;j++) m[j]++;
        } else {
            for (j=l;j<=r;j++) f[j]++;
        }
    }
    for (i=1;i<370;i++)
    if (2*min(m[i],f[i])>ans) ans=2*min(m[i],f[i]);
    printf("%d\n", ans);
    return 0;
}


problemC:给定一组n,k,然后给一个长度为k的括号字符串s求在s的左右两边添加括号,最终有多少种方案能使添加后的字符串合理,合理的添加为最终'('的数量等于')'的数量并且从左往右统计的时候'('的数量永远大于等于')'的数量。

分析:首先我们应该统计出左边至少要加多少个'(',右边至少要加多少个')',比如说给定的s="()))(()",那么我们左边至少要添加2个'('右边至少要添加1个')'。知道了这一点后我们预处理出dp[i][j],dp[i][j]表示的是长度为i的合理括号字符串中'('的数量比')'的数量多j个。假设我们左边至少添加L个右边至少添加R个,那么我们接下来枚举dp[i][j]*dp[n-k-i][j-L+R],0<=i<=n-k,j>=L。dp[n-k-i][j-L+R]是s的右边添加的,也可以用预处理的dp数组,意义的话把这个字符串反转一下就行了。O((n-k)^2)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=2010;
const int MAX=151;
const int MOD1=1000000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
char s[100010];
int g[N][N];
void deal() {
    int i,j;
    memset(g,0,sizeof(g));
    g[0][0]=1;
    for (i=0;i<=2000;i++)
        for (j=0;j<=2000;j++)
        if (g[i][j]) {
            g[i+1][j-1]=(g[i+1][j-1]+g[i][j])%MOD1;
            g[i+1][j+1]=(g[i+1][j+1]+g[i][j])%MOD1;
        }
}
int main()
{
    int i,j,n,m,l,r;
    ll ans=0;
    scanf("%d%d", &n, &m);
    scanf("%s", s);
    deal();l=r=0;
    for (i=0;i<m;i++)
    if (s[i]=='(') r++;
    else { r--;l=min(r,l); }
    r-=l;l=-l;
    for (i=l;i<=n-m;i++)
        for (j=l;j<=i;j++)
        if (j-l+r<=n-m) ans=(ans+(ll)g[i][j]*g[n-m-i][j-l+r])%MOD;
    printf("%I64d\n", (ans+MOD)%MOD);
    return 0;
}

problemD:给你n个蛋糕的半径r和高h,让你做一个体积最大的组合蛋糕,组合的要求为编号小的在上面,并且在下面的蛋糕体积要严格大于上面的蛋糕。

分析:初步一看,很明显的LIS的模型,但是又有一点点的不同,这个并不能用传统的那种继承方法。但是大体上还是不变的找体积比自己小的并且已经积累的体积最大的蛋糕。先离散化一下r*r*h,再用树状数组或线段树优化下转移即可。O(n*logn)

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
ll v[N],d[N],f[N];
void add(int a,ll b) {
    for (;a<N;a+=a&(-a)) f[a]=max(f[a],b);
}
ll get(int a) {
    ll ret=0;
    for (;a;a-=a&(-a)) ret=max(ret,f[a]);
    return ret;
}
int main()
{
    int i,n,w,u;
    ll r,h;
    scanf("%d", &n);
    for (i=1;i<=n;i++) {
        scanf("%I64d%I64d", &r, &h);
        v[i]=r*r*h;d[i]=v[i];
    }
    sort(d+1,d+n+1);u=unique(d+1,d+n+1)-(d+1);
    memset(f,0,sizeof(f));
    for (i=1;i<=n;i++) {
        w=lower_bound(d+1,d+u+1,v[i])-d;
        add(w,get(w-1)+v[i]);
    }
    printf("%.10f\n", get(N)*acos(-1));
    return 0;
}

problemE:给定一组n,m,表示n个节点的树和m个询问,接下来是n-1条边和m组询问,每组询问为u,v,求在树中添加一条边使得u到v这条路径在简单环上的期望。

分析:要求u到v这条路径在简单环上即添加的边的两个点分别在u和v的两侧,设u这边有a个节点v这边有b个节点,那么简单环的总方案数为a*b。路径长度之和我们可以分为3部分来求,1:u这边的路径和*b。2:v这边的路径和*a。3:u,v之间的路径和*a*b,然后总和/a/b。分析完题目就简单了,我们用dfs预处理出so[i]表示以1为根的情况下i这颗子树的节点数,sum[i]表示以1为根的情况下i这颗子树下的路径和,sumall[i]表示n的点到i的路径和,然后加上lca的情况讨论一下即可。O(m*logn)

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<math.h>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
const int N=100010;
const int MAX=151;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll INF=10000000010;
const ll MOD=1000000007;
typedef unsigned long long ull;
int tot,u[N],v[2*N],pre[2*N];
void add(int a,int b) {
    v[tot]=b;pre[tot]=u[a];u[a]=tot++;
}
int n,bo,d[N],so[N],fa[N][25];
ll sum[N],sumall[N];
void dfs1(int a,int f) {
    d[a]=d[f]+1;so[a]=1;sum[a]=0;fa[a][0]=f;
    for (int i=1;i<24;i++)
    if (d[a]<=1<<i) break ;
    else fa[a][i]=fa[fa[a][i-1]][i-1];
    for (int i=u[a];i!=-1;i=pre[i]) {
        if (v[i]==f) continue ;
        dfs1(v[i],a);
        so[a]+=so[v[i]];sum[a]+=sum[v[i]]+so[v[i]];
    }
}
void dfs2(int a,int f) {
    sumall[a]=sumall[f]-so[a]+n-so[a];
    for (int i=u[a];i!=-1;i=pre[i]) {
        if (v[i]==f) continue ;
        dfs2(v[i],a);
    }
}
int lca(int a,int b) {
    for (int i=24;i>=0;i--)
    if (d[a]+(1<<i)<=d[b]) b=fa[b][i];
    if (a==b) return a;
    for (int i=24;i>=0;i--)
    if ((1<<i)<d[a]&&fa[a][i]!=fa[b][i]) {
        a=fa[a][i];b=fa[b][i];
    }
    return fa[a][0];
}
void query(int a,int b) {
    int i,c;
    double ans;
    if (d[a]>d[b]) swap(a,b);
    c=lca(a,b);
    ans=(double)d[a]+d[b]-2*d[c]+1;
    if (a==c) {
        c=b;
        for (i=24;i>=0;i--)
        if (d[a]+(1<<i)<d[c]) c=fa[c][i];
        ans+=(double)(sumall[a]-sum[c]-so[c])/(n-so[c]);
        ans+=(double)sum[b]/so[b];
    } else {
        ans+=(double)sum[a]/so[a];
        ans+=(double)sum[b]/so[b];
    }
    printf("%.7f\n", ans);
}
int main()
{
    int a,b,i,m;
    scanf("%d%d", &n, &m);
    tot=0;memset(u,-1,sizeof(u));
    for (i=1;i<n;i++) {
        scanf("%d%d", &a, &b);
        add(a,b);add(b,a);
    }
    d[1]=0;dfs1(1,1);sumall[1]=sum[1];
    for (i=u[1];i!=-1;i=pre[i]) dfs2(v[i],1);
    while (m--) {
        scanf("%d%d", &a, &b);
        query(a,b);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值