2016年多校第5场

简单记录一下多校第5场的情况吧,先贴一下赛中的情况,赛后写的题以后再更新吧。就按照过题的顺序吧(可能只是一个比赛过程的记录,便不会细讲思路)。

上来先看了前几个题的题意,感觉这场题意表述还是很清楚的(稍后就被打脸)。

先看有人过了第三题,就去看第三题了,结果觉得是个贪心水题。

题目链接:http://acm.hdu.edu.cn/contests/contest_showproblem.php?cid=708&pid=1003

解法:我是模拟了一个栈的情况,元素为正,入栈,为负,加上栈顶元素,直到为正。

代码如下:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int n;

const int maxn = 1e6 + 5;

int a[maxn];

typedef  long long ll;

ll sum[maxn];
int top;


int main(int argc, const char * argv[]) {
    while(scanf("%d",&n) == 1){
        for(int i = 1;i <= n;i++) scanf("%d",&a[i]);
        top = 0;
        for(int i = 1;i <= n;i++){
            if(a[i] >= 0) sum[++top] = (ll)a[i];
            else{
                ll s = a[i];
                while(top > 0 && s < 0){
                    s += sum[top];
                    top--;
                }
                sum[++top] = s;
            }
        }
        cout << top << endl;
    }
    return 0;
}

然后游荡了一会儿,发现1011也开始有人过了,就去看1011,研究了以后发现是个dp,写了个状态方程,如下。

dp[i][j] = dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1];
if(a[i] == b[j]) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + 1);

还是很好理解的,但很悲惨的是CE了一发,IDE的锅。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 1005;

int n,m;

int a[maxn],b[maxn];

typedef  long long ll;
const ll mod = 1e9 + 7;

ll dp[maxn][maxn];

int main(int argc, const char * argv[]) {
    while(scanf("%d%d",&n,&m) == 2){
    for(int i = 1;i <= n;i++) scanf("%d",a + i);
    for(int i = 1;i <= m;i++) scanf("%d",b + i);
    for(int i = 0;i < maxn;i++) dp[i][0] = dp[0][i] = 0;
    for(int i = 1;i <= n;i++){
        for(int j = 1;j <= m;j++){
            dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1] + mod) % mod;
            if(a[i] == b[j]) dp[i][j] = (dp[i][j] + dp[i - 1][j - 1] + 1) % mod;
        }
    }
    cout << dp[n][m] << endl;
    }
    return 0;
}

过完1011之后,12也已经陆陆续续有很多人过了,一开始以为这题做过,但仔细一看并没有,在仔细的分析之后,得出公式,就是所有的逆序对个数乘上所有的顺序对个数再减去每个点所在的逆序对数乘顺序对数,然后开始写。

决定采用线段树,因为这样可以较好的计算左边比它小和右边比它大的,但是一开始懒,就直接建了四棵树,第一次交上去RE,说爆栈。后来改成建两棵树,就过了,而且空间差了很多,感觉很神奇,可能是自己一开始写错了。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

const int maxn = 50000 + 5;

int sumv[maxn << 2];

void build(int o,int l,int r){
    int m = (l + r) >> 1;
    if(l == r) sumv[o] = 0;
    else{
        build(o * 2,l,m);
        build(o * 2 + 1,m + 1,r);
        sumv[o] = sumv[o * 2] + sumv[o * 2 + 1];
    }
}

void update(int o,int l,int r,int p){
    int m = (l + r) >> 1;
    if(l == r) sumv[o] = 1;
    else{
        if(p <= m) update(o * 2, l, m, p);
        else update(o * 2 + 1, m + 1, r, p);
        sumv[o] = sumv[o * 2] + sumv[o * 2 + 1];
    }
}

int query(int o,int l,int r,int ql,int qr){
    if(ql > qr) return 0;
    int m = (l + r) >> 1;
    int ans = 0;
    if(ql <= l && r <= qr) return sumv[o];
    if(ql <= m) ans += query(o * 2,l,m,ql,qr);
    if(m < qr) ans += query(o * 2 + 1,m + 1,r,ql,qr);
    return ans;
}

struct st{
    int id;
    int key;
    bool operator < (const st & rhs) const{
        return key < rhs.key || (key == rhs.key && id < rhs.id);
    }
}a[maxn];

bool cmp1(st a,st b){
    return a.key > b.key;
}

int n;

int ni[maxn],shun[maxn];//右边比它小的,左边比它小的
int same[maxn];
int l[maxn],r[maxn];//右边比它大的,左边比它大的

int main(int argc, const char * argv[]) {
    while(scanf("%d",&n) == 1){
        for(int i = 1;i <= n;i++){
            scanf("%d",&a[i].key);
            a[i].id = i;
        }
        build(1,1,n);
        sort(a + 1,a + 1 + n);
        for(int i = 1,j = 1;i <= n;i = j + 1,j = i){
            while(j < n && a[j].key == a[j + 1].key) j++;
            for(int k = i;k <= j;k++){
                same[a[k].id] = j - i + 1;
                shun[a[k].id] = query(1, 1, n, 1, a[k].id - 1);
            }
            for(int k = i;k <= j;k++){
                update(1, 1, n, a[k].id);
            }
        }
        sort(a + 1,a + 1 + n,cmp1);
        build(1,1,n);
        for(int i = 1,j = 1;i <= n;i = j + 1,j = i){
            while(j < n && a[j].key == a[j + 1].key) j++;
            for(int k = i;k <= j;k++){
                l[a[k].id] = query(1,1,n,a[k].id + 1,n);
            }
            for(int k = i;k <= j;k++){
                update(1, 1, n, a[k].id);
            }
        }
        long long sum = 0;
        long long sum1 = 0;
        for(int i = 1;i <= n;i++){
            sum += shun[i] + l[i];
            sum1 += n - shun[i] - l[i] - same[i];
        }
        sum /= 2;
        sum1 /= 2;

//        for(int i = 1;i <= n;i++){
//            cout << ni[i] << " "<< shun[i] << " " << l[i] << " " << r[i] << endl;
//        }
//        for(int i = 1;i <= n;i++) cout << l[i] << " " << shun[i] << " " << same[i] << endl;
        long long ans = sum * sum1;
        for(int i = 1;i <= n;i++){
            long long n1 = shun[i] + l[i];
            long long n2 = n - n1 - same[i];
//            cout << n1 << " " << n2 << endl;
//            cout << ans << endl;
            ans -= n1 * n2;
        }
        cout << ans << endl;
    }
    return 0;
}

过了三个题以后开始迷茫,看大家开始过第一题,我看了一会儿没有好的思路,觉得是个N^3的题,是个dp。后来yy了个状态转移方程,就是题解中的那个啦,但是由于这题题意我一开始没理解透彻,初值设错了,所以样例一直没过,后来经jfs学长点拨,外加告诉我w不可能很大(早该想到的,太年轻),就过了这题。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>

using namespace std;

const int maxn = 2005;

double dp[maxn][maxn];

int k,w;

int main(int argc, const char * argv[]) {

    dp[0][1] = 0;
    for(int i = 1;i <= 2000;i++) dp[i][1] = 1.0 * i / (i + 1.0) * dp[i - 1][1] + 1.0;
    for(int j = 2;j <= 30;j++){
        dp[0][j] = 0;
        for(int i = 1;i <= 2000;i++){
            for(int l = 1;l <= i;l++){
                double t = 1.0 * (i - l + 1) / (1.0 * i + 1) * dp[i - l][j] + 1.0 * l / (1.0 * i + 1) * dp[l - 1][j - 1] + 1.0;
                if(l == 1 || t < dp[i][j]){
                    dp[i][j] = t;
                }
            }
        }
    }
    while(scanf("%d%d",&k,&w) == 2){
        w = min(w,30);
        printf("%.6lf\n",dp[k][w]);
    }
    return 0;
}

由于手残和各种问题,过完4个题已经是3个多小时了(哎,好菜啊),额,点开场上现在过的人最多的第7题,一看题,是个数位dp类型的,正巧前一场51nod刚写过类似的,还写了好久,有了悲伤的经验,就开始写,这次倒是写的很顺利,但也加上各种时间(debug也搞了好久)用了1个小时。悲剧的是又CE了一发。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>

using namespace std;

int k;

typedef long long ll;

ll l,r;

ll f[20];//第一个数字随便
ll f1[20];//第一个数字不为0
ll f2[6][20];//前面有数字限制

bool vis[10];
int len;
int dig[20];

ll dfs(int pos){
    if(pos == 0){//所有位和原来一样
        memset(vis,false,sizeof(vis));
        for(int i = 0;i < len;i++){
            if(i >= k) vis[dig[i - k]] = false;
            if(vis[dig[i]]) return 0;
            else vis[dig[i]] = true;
        }
        return 1;
    }
    ll ret = 0;
    if(pos + k <= len){//
        vis[dig[pos + k - 1]] = false;
        for(int i = 0;i < dig[pos - 1];i++){
            if(vis[i]) continue;
            else{
                ret += f2[k - 1][pos - 1];
            }
        }
    }else{
        for(int i = 0;i < dig[pos - 1];i++){
            if(vis[i]) continue;
            else{
                ret += f2[min(k - 1,len - pos + 1)][pos - 1];
                //cout << ret << endl;
            }
        }
    }
    if(vis[dig[pos - 1]]) return ret;
    vis[dig[pos - 1]] = true;
    ret = ret + dfs(pos - 1);
    return ret;
}

ll solve(ll n){
    if(n == 0) return 0;
    //if(n == 1e18) n--;
    len = 0;
    while(n){
        dig[len++] = n % 10;
        n /= 10;
    }
    ll ans = 0;
    for(int i = 1;i < len;i++) ans += f1[i];
    for(int i = 1;i < dig[len - 1];i++){
        ans += f2[1][len - 1];
    }
    //cout << ans << endl;
    memset(vis,false,sizeof(vis));
    vis[dig[len - 1]] = true;
    ans = (ans + dfs(len - 1));
    return ans;
}

int main(int argc, const char * argv[]) {
    while(cin >> l >> r >> k){
        f1[1] = 9;
        f[1] = 10;
        for(int i = 2;i <= k;i++){
            f1[i] = f1[i - 1] *  (ll)(11 - i);
            f[i] = f[i - 1] * (ll)(11 - i);
        }
        for(int i = k + 1;i <= 19;i++){
            f1[i] = f1[i - 1] * (ll)(11 - k);
            f[i] =f[i - 1] * (ll)(11 - k);
        }
        for(int i = 1;i <= 19;i++){
//            cout << f1[i] << " " << f[i] << endl;
        }
        for(int i = 1;i < k;i++){
            f2[i][1] = 10 - i;
            f2[i][0] = 1;
            for(int j = 2;j + i <= k;j++){
                f2[i][j] = f2[i][j - 1] * (11 - i - j);
            }
            for(int j = k - i + 1;j <= 19;j++){
                f2[i][j] = f2[i][j - 1] * (11 - k);
            }
        }
//        for(int i = 1;i < k;i++){
//            for(int j = 1;j <= 19;j++) cout << f2[i][j] << " ";
//            cout << endl;
//        }
        //cout << solve(100) << endl;
        //cout << solve(19) << endl;
        cout << (solve(r) - solve(l - 1)) << endl;
    }
    return 0;
}

总之这一场是前5场我最充实的一场吧,感觉一直没闲下来,有题想,有题可做。然后这场是继第三场之后又打进前100名,还是很开心的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值