2017/11/5模拟赛总结

来源:互联网 发布:马里亚纳网络为何恐怖 编辑:程序博客网 时间:2024/06/17 15:24

T1 放数字游戏

可以通过考虑一个点和前面某个点的贡献入手
假设当前前面的点为x 后面的点为y(前后均指下标)
那么xy的相对位置有两种
x前面的数都可以凑出任意顺序的排列
y后面的数也一样
xy之间的数只有一种排列方式 即按顺序排
所以可以枚举xy 用排列数解决
显然x作为答案的贡献可以用前缀和维护
那么扫一遍就可以了
O(n)

#include<bits/stdc++.h>using namespace std;#define N 200010#define mo 1000000007int n,a[N];int Pow[N];void init(){    int i;    Pow[0]=1;    for (i=1;i<=n;i++) Pow[i]=Pow[i-1]*2%mo;}        int main(){    int i;    scanf("%d",&n);    for (i=0;i<=n;i++) scanf("%d",&a[i]);    init();    int sum=a[0],ans=0;    for (i=1;i<=n;i++){        ans=(ans+2LL*sum*a[i]%mo*Pow[n-i])%mo;        sum=(sum+1LL*a[i]*Pow[i-1])%mo;    }    printf("%d\n",ans);    return 0;}


T2 电缆建设

这是一个平面上的最小生成树问题
特殊之处在于所有点都排成两列

解法1

直接建边是O(n2)
一个点如果要和对面的点相连 一定和纵坐标相邻的点连边更优
那么可以建一颗有4n条边的树 跑Kruskal算法
但是由于排序需要较多时间 会被卡常

解法2

考虑dp
dpi,j,k表示左边的第i个点 和右边的第j个点下面已经联通 ij的联通状态为k 时需要的最小花费
dpi,j,1=min(dpi1,j,1+aiai1,dpi1,j,1+dis(i,j),dpi1,j,0+aiai1+dis(i,j))
dpi,j,0=min(dpi1,j,1,dpi1,j,0+aiai1)
由于只有相邻的边连边可能作为答案 所以可以时刻让ij处于相邻位置
然后可以同时滚动ij两维
这样dp数组只需要222的空间
O(n+m)
第一次写同时滚动两维的dp 比较麻烦
要注意数组的清空问题

#include<bits/stdc++.h>using namespace std;#define N 1000010#define EPS 1e-6inline void rd(int &x){    char c;x=0;    while (c=getchar(),c<48);    do x=(x<<1)+(x<<3)+(c^48);    while (c=getchar(),c>=48);}int n,m,a[N],b[N];double dp[2][2][2];long long X;#define dis(x,y) sqrt(X+1LL*(a[x]-b[y])*(a[x]-b[y]))inline void Dp(int f1,int f2,int i,int j){    dp[f1][f2][0]=dp[f1][f2][1]=1e9;    dp[!f1][!f2][0]=dp[!f1][!f2][1]=1e9;    dp[f1][f2][0]=min(dp[f1][f2][0],min(dp[!f1][f2][0]+(a[i]-a[i-1]),dp[!f1][f2][1]));    dp[f1][f2][0]=min(dp[f1][f2][0],min(dp[f1][!f2][0]+(b[j]-b[j-1]),dp[f1][!f2][1]));    dp[f1][f2][1]=min(dp[f1][f2][1],min(min(dp[!f1][f2][1]+(a[i]-a[i-1]),dp[!f1][f2][1]+dis(i,j)),dp[!f1][f2][0]+(a[i]-a[i-1])+dis(i,j)));    dp[f1][f2][1]=min(dp[f1][f2][1],min(min(dp[f1][!f2][1]+(b[j]-b[j-1]),dp[f1][!f2][1]+dis(i,j)),dp[f1][!f2][0]+(b[j]-b[j-1])+dis(i,j)));}       int main(){    int i,j,X1,X2;    rd(n),rd(m),rd(X1),rd(X2);    X=1LL*(X1-X2)*(X1-X2);    for (i=1;i<=n;i++) rd(a[i]),a[i]+=a[i-1];    for (i=1;i<=m;i++) rd(b[i]),b[i]+=b[i-1];    dp[0][0][0]=dp[0][0][1]=dp[0][1][0]=dp[0][1][1]=dp[1][0][0]=dp[1][0][1]=1e9;    dp[1][1][0]=0;    dp[1][1][1]=dis(1,1);    int f1=1,f2=1;    for (i=1,j=1;i<=n;i++,f1^=1){        while (j<=m && b[j-1]<=a[i]){            if (i==1 && j==1){                j++; f2^=1;                continue;            }            Dp(f1,f2,i,j);            j++; f2^=1;        }        j--; f2^=1;    }    f1^=1;    for (j=j+1,f2^=1;j<=m;j++,f2^=1) Dp(f1,f2,n,j);    f2^=1;    printf("%.2f\n",dp[f1][f2][1]);    return 0;}


T3 抢座位

首先有一个比较显然的性质:若第i个人本来要到j位置 现在只能到k位置 那么在jk之间的人一定编号比i
可以dfs每个固定位置的人最后在什么位置
那么每次需要知道当前已经有人的位置 和已经选好位置的人数
在比当前人编号小的人里面 可以选出一些在当前人的前面
这些人的位置又不固定
所以要用排列组合算出方案数
全部确定完之后可能还有人没有固定 要再排列一下
而由于可以通过可行性剪枝剪掉很多不合法的情况 dfs还是很快的
位置的状态可以用二进制压位表示
极限为O(nnm)

#include<bits/stdc++.h>using namespace std;#define N 35int n,m,mo,C[N][N],fac[N];struct node{    int x,y;    bool operator <(const node &_)const{        return x<_.x;    }}a[N];bool mark[N];int ans;void init(){    int i,j;    for (i=0;i<=n;i++){        C[i][0]=1;        for (j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo;    }    fac[0]=1;    for (i=1;i<=n;i++) fac[i]=1LL*fac[i-1]*i%mo;}void dfs(int l,int sum,int Mk){    if (l>m){        ans=(ans+1LL*sum*fac[n-Mk])%mo;        return;    }    int i,cnt=0,tmp=0;    bool cop[N];    for (i=a[l].y;i<=a[l].x+a[l].y-1 && i<=n;i++){        if (mark[i]){            cnt++;            continue;        }        mark[i]=1; tmp++;        memcpy(cop,mark,sizeof(cop));        if (a[l].x-1-Mk<0 || i-a[l].y-cnt<0) return;        dfs(l+1,1LL*sum*C[a[l].x-1-Mk][i-a[l].y-cnt]%mo*fac[i-a[l].y-cnt]%mo,Mk+tmp);        memcpy(mark,cop,sizeof(cop));    }}int main(){    int i;    scanf("%d%d%d",&n,&m,&mo);    for (i=1;i<=m;i++) scanf("%d%d",&a[i].x,&a[i].y);    init();    sort(a+1,a+1+m);    dfs(1,1,0);    printf("%d\n",ans);    return 0;}


NOIP2017 倒计时6天
今天没有犯低级错误 只是第二题常数太大
排序时尽量不要用double 比如根号可以先存longlong下的没开根的数
因为不判EPS可能会出现精度问题
判了又会使排序常数大大增加

Date:2017/11/5
By CalvinJin