Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition)

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

problemA. Bear and Game:看90分钟电视,有n个有趣点,如果持续15没有看到有趣的点,那么就关电视,问什么时候会关掉电视。

分析:水题。找到第一个跨度大于15的点即可,注意答案最大90。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#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=500010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
typedef double db;
typedef unsigned long long ull;
int a[100];
int main()
{
    int i,n,ans=0;
    scanf("%d", &n);
    for (i=1;i<=n;i++) scanf("%d", &a[i]);
    sort(a+1,a+n+1);a[0]=0;
    for (i=1;i<=n;i++)
    if (a[i]-a[i-1]>15) break ;
    else ans=a[i];
    printf("%d\n", min(ans+15,90));
    return 0;
}

problemB. Problems for Round:给定n个人,编号1~n,将这n个人分为两组,给定m个关系,每组关系a,b表示a和b不能在同一组,并且所有关系中较小的在一组,较大的在一组。在较小组中的数要小于任何一个在较大组的数,问有多少种分法。

分析:较小组中存下最大值,较大组中存下最小值,看中间有多少个数是自由的即可,注意关系m==0的情况。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#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=500010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
typedef double db;
typedef unsigned long long ull;
int main()
{
    int i,n,m,mi,mx,x,y;
    scanf("%d%d", &n, &m);
    mi=0;mx=n+1;
    for (i=1;i<=m;i++) {
        scanf("%d%d", &x, &y);
        if (x>y) swap(x,y);
        mi=max(mi,x);mx=min(mx,y);
    }
    if (mi==0) { printf("%d\n", n-1);return 0; }
    if (mx<mi) printf("0\n");
    else printf("%d\n", mx-mi);
    return 0;
}

problemC. Bear and Colors:给定n个数,每个数都在1~n以内,会形成n*(n+1)/2个区间,设x为区间的众数(多个众数取最小的那一个),然后ans[x]++。求统计完所有区间后的ans数组。

分析:固定区间左端点L,然后扩展右端点R,没扩展一次得到一个众数x就ans[x]++即可。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#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=5010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
typedef double db;
typedef unsigned long long ull;
int a[N],ans[N],num[N];
int main()
{
    int i,j,n,g,co;
    scanf("%d", &n);
    for (i=1;i<=n;i++) scanf("%d", &a[i]);
    for (i=1;i<=n;i++) {
        g=0;co=0;
        for (j=i;j<=n;j++) {
            num[a[j]]++;
            if (num[a[j]]>g) { g=num[a[j]];co=a[j]; }
            if (num[a[j]]==g&&a[j]<co) { g=num[a[j]];co=a[j]; }
            ans[co]++;
        }
        for (j=i;j<=n;j++) num[a[j]]--;
    }
    for (i=1;i<=n;i++) printf("%d ", ans[i]);
    printf("\n");
    return 0;
}

problemD. Bear and Two Paths:给定点数n和k,a,b,c,d。表示要构造出两条长度为n的链v1-v2-...-vn,u1-u2-...-un。v和u都是n的一个排列,表示路径中点的顺序。其中v1==a&&vn==b和u1==c&&un==d且a-b不能直接用边相连,c-d不能直接用边相连。要求最多只能构造k条边(双向边)。无解输出-1,有解输出v和u。

分析:因为要在k条边以内构造出来,我们能想到应该有最优的构造方法。我们再想想无解的情况首先考虑点的约束:很容易想到n==4是无解的,n>4时都能用一个辅助点达到要求然后只要看边的约束。边的约束:首先我们能想到这两条路径要构出至少2个环且最好将能重复使用的边重复使用。知道这些之后画画图就能想出这个模型:构造一个不属于(a,b,c,d)的点e作为辅助点,构造6条边:a-e,a-c,c-e,b-d,b-e,d-e。然后对于多出的点我们只要将它们连成链然后插入到这6条边中任意一个位置即可。这显然是最优的n>4&&边为n+1。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#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=500010;
const int MAX=1000000100;
const int mod=100000000;
const int MOD1=1000000007;
const int MOD2=1000000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=998244353;
const int INF=1000000010;
typedef double db;
typedef unsigned long long ull;
int p[1010];
int main()
{
    int a,b,c,d,e,i,n,k;
    scanf("%d%d", &n, &k);
    scanf("%d%d%d%d", &a, &b, &c, &d);
    memset(p,0,sizeof(p));
    p[a]=p[b]=p[c]=p[d]=1;
    for (i=1;i<=n;i++)
    if (!p[i]) { e=i;p[i]=1;break ; }
    if (n<5||k<n+1) printf("-1\n");
    else {
        printf("%d ", a);
        for (i=1;i<=n;i++)
        if (!p[i]) printf("%d ", i);
        printf("%d %d %d %d\n", c, e, d, b);
        printf("%d ", c);
        for (i=n;i;i--)
        if (!p[i]) printf("%d ", i);
        printf("%d %d %d %d\n", a, e, b, d);
    }
    return 0;
}

problemE. Levels and Regions:给定n个数t[1]~t[n],给定组数k,将这n个数分为k组,然后求一个期望。规则:1:同一组的元素是连续的,且组不能为空。2:每次找到第一个组(有元素未被杀死),设最小的未死元素为X,不存在则结束游戏。3:对于选定的组G和元素X,将G中杀死的元素i放t[i]个小球到抽奖箱,放t[X]个小球到抽奖箱,然后等概率的抽一个小球,这个操作花费1小时,抽到的小球代表的元素被杀死(如果是已经被杀死就跳过)。要你进行分组,使得所有元素都被杀死需要花费的时间的期望最小。

分析:首先因为同一组只能是连续的元素,我们就要会算连续的[l,r]成为一组是全部被杀死的期望时间,最基础的期望问题,设期望为F[l,r],那么会有F[l,r]=sigma(sum[i]/t[i]-sum[l-1]/t[i]),{l<=i<=r},那么我们就能设dp[i][j]表示将前j个元素分为i个组花费的最小期望。那么状态转移方程式为:dp[i][j]=min(dp[i][k]+F[k,j]),我们设Si=sigma(1<=j<=i)t[j],Qi=sigma(1<=j<=i)Sj/tj,Ri=sigma(1<=j<=i)1/tj。然后我们将dp[i][j]中的F[k,j]用这些表达式替换能得到:dp[i][j]=min(dp[i][k]-Qk+Sk*Rk-Sk*Rj+Qj),这里很明显的斜率优化了。维护一个下凸包就能在O(1)的时间中转移状态啦。O(n*k)。

代码:

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<bitset>
#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=1000010;
const int MAX=151;
const int mod=100000000;
const int MOD1=100000007;
const int MOD2=100000009;
const double EPS=0.00000001;
typedef long long ll;
const ll MOD=1000000000;
const ll INF=1000000000010;
typedef double db;
typedef long double ldb;
typedef unsigned long long ull;
ll hea,top,t[N];
ldb dx[N],dy[N],dp[2][N],sum[N],sump[N],sumq[N];
int cala(ldb x,ldb y,ldb x1,ldb y1,ldb x2,ldb y2) {
    return (y2-y1)*(x1-x)>=(y1-y)*(x2-x1);
}
void add(ldb x,ldb y) {
    if (top-hea<1) { top++;dx[top]=x;dy[top]=y;return ; }
    while (top-hea>0&&cala(x,y,dx[top],dy[top],dx[top-1],dy[top-1])) top--;
    top++;dx[top]=x;dy[top]=y;
}
int main()
{
    int i,j,n,k,now,pre;
    scanf("%d%d", &n, &k);
    for (i=1;i<=n;i++) scanf("%I64d", &t[i]);
    t[0]=0ll;sum[0]=sump[0]=sumq[0]=0.0;
    for (i=1;i<=n;i++) {
        sum[i]=sum[i-1]+1.0*t[i];
        sumq[i]=sumq[i-1]+1.0/t[i];
        sump[i]=sump[i-1]+sum[i]*1.0/t[i];
    }
    now=pre=0;
    for (i=0;i<=n;i++) dp[now][i]=sump[i];
    for (i=2;i<=k;i++) {
        pre=now;now^=1;hea=1;top=0;
        for (j=i;j<=n;j++) {
            add(sum[j-1],sum[j-1]*sumq[j-1]-sump[j-1]+dp[pre][j-1]);
            while (top-hea>0&&sumq[j]*(dx[hea]-dx[hea+1])<=dy[hea]-dy[hea+1]) hea++;
            dp[now][j]=sump[j]-dx[hea]*sumq[j]+dy[hea];
        }
    }
    printf("%.15f\n", (db)dp[now][n]);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值