UESTC 2014 Summer Training #7 Div.2

  DAY7一开始状态还行,最高纪录rank7,然后没力气了,一直跌到rank24,问题还是很多呐。昨天总结的问题依然很大。

Problem A  UVA 12377

  就一开始还是慌了,没审清楚题意就去WA了一发。

  大意是给你一个数字表示的字符串,最高20位,第一表示有N个质数,后面是其幂的非降序排列,所求能表示的数个数。

  简单的排列组合题,不过需要处理出2~END的不同划分,并且满足非降序,用dfs处理即可。

  具体就是dfs过程中记录下每一个数字出现的次数,因为相同的次数是不计顺序的,需要在统计时除以cnt!(阶乘)

  对于每一种划分,增加的方案数 = 9!/(9-N+1)!/cnt[i]!

  我的做法是dfs字符串s,上一个数x, 当前划分出的个数n,每次按照剩余的长度枚举当前划分出的数字,并且满足不小于上一个数x,满足的话,

  还需要纪录下其出现的次数,最后dfs的串为空的时候检查划分出的个数,满足开始统计

  

  下午在做的时候,也出现了很多小问题:

  1.划分串长达19位,如果个数为1,2之类,转化为整数就可能会爆炸,需要用long long,一组数据 211...(19个1)

  2.fgets整行读入,字符串末尾带上了\n,需要自己处理掉,不然strlen返回值不是理想结果

  3.!!!!!!!!!!!!!!!!中间可能会出现0,而0不能作为一个数的前导,包括0,所以如果dfs枚举出字符串的前导为0时,中断(<---下午没A的原因

  这道题自己的问题还是很大的,一开始的审题不仔细,对数据考察的不深入,爆int,以及最后默认没有0,都是很难解决的"小毛病",却是很致命的.

  

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 25;
const int A[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800};

int T, tot;
long long numcnt[maxn], hashnum[maxn];
long long ans;
char code[maxn];

void dfs(char *s, long long x, int num, int upper)
{
    if(*s == 0 && upper == num) {
        long long cnt = 1;
        for(int i = 0; i < upper; i++)
            cnt *= (9-i);
        for(int i = 0; i < tot; i++)
            cnt /= A[numcnt[i]];
//        cout << tot << endl;
//        cout << "cnt" <<  cnt << endl;
        ans += cnt;
    }
    int len = (int)strlen(s);
//    cout << x << ‘ ‘ << len << endl;
    for(int i = 1; i <= len; i++) {
        long long _last;
        char tmp[maxn];
        strncpy(tmp, s, sizeof(char)*i);
        tmp[i] = 0;
        sscanf(tmp, "%lld", &_last);
        if(_last < x || tmp[0] == ‘0‘)    continue;
        int pos;
        bool has = false;
        for(int j = 0; j < tot; j++)
            if(hashnum[j] == _last) {
                has = true;
                numcnt[j]++;
                pos = j;
                break;
            }
        if(!has) {
            hashnum[tot] = _last;
            pos = tot++;
            numcnt[pos] = 1;
        }
        dfs(s+i, _last, num+1, upper);
        numcnt[pos]--;
        if(!has)    tot--;
    }
}

int main()
{
#ifdef LOCAL
    freopen("A.in", "r", stdin);
#endif
    scanf("%d", &T);
    getchar();
    while(T--) {
        fgets(code, maxn, stdin);
        int len = strlen(code);
        while(code[len-1] < ‘0‘ || code[len-1] > ‘9‘) {
            code[len-1] = 0;
            len--;
        }
        tot = 0;
        ans = 0;
        memset(hashnum, 0, sizeof(hashnum));
        memset(numcnt, 0, sizeof(numcnt));
        dfs(code+1, 0, 0, code[0]-‘0‘);
//        cout << cnt << endl;
        printf("%lld\n", ans);
    }
    return 0;
}

Problem F  UVA 12382

  对于一个M*N的01矩阵,给出每行每列要求出现1的最少个数,球要满足条件填入1的最少个数。

  

  贪心的原理还是不是很理解,以下废话只是个人的一些思考

(废话->)由于交叉点的1可以同时满足行列,就看怎么让交叉点最多,比如以填行同时使交叉点最多,可以对行需求排序,从高到低放置1,放置第i行的时候,为了使得

下一行,能够放置的列数更多(交叉情况)(避免列不够而这行没有满足需要添加不交叉点),如果列放置需求(列数)已经小于这行需求,就在这行其他位置再填入一些1

最后可能有些列没满足,加上即可

  由于需要取出需求最大的列,用优先队列维护,注意是k列都取出后,再减一放进去,而不是边pop边push,小于等于0的列就不用push了

/**Updated**/

用模拟的方法去实现,我们肯定要将每行都至少放置需要的个数满足,即每行放置需求个数的1,现在考虑这些1放在哪些位置,贪心的策略是,选择需求最多的列来放置,这样可以保证在做下一行的时候,能够选择的列更多(举个列子,列需求1 2,行需求1 2 ...,如果第一行放置1 0,第二行就只能放置为0 1,再去放上1个不交叉的,这种策略就不是最优的:保证尽量多的交叉点)

/**Updated**/

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <vector>

using namespace std;

const int maxn = 1000+50;

int T, m, n, ans;
priority_queue<int, vector<int>, less<int> > q;

int main()
{
#ifdef LOCAL
    freopen("I.in", "r", stdin);
#endif
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &m, &n);
        for(int i = 0; i < m; i++) {
            int tmp;
            scanf("%d", &tmp);
            q.push(tmp);
        }
//        cout << "top " << q.top() << endl;
        ans = 0;
        for(int i = 0; i < n; i++) {
            int col;
            scanf("%d", &col);
            int tot = 0;
            int tofill[maxn];
            for(int j = 0; j < col; j++)
                if(!q.empty()) {
                    tofill[tot++] = q.top();    q.pop();
                }
            for(int j = 0; j < tot; j++)
                if(tofill[j] > 1)    q.push(tofill[j]-1);
            ans += col;
//            if(T == 1)    cout << ans << endl;
        }
        while(!q.empty())    {
//            if(T == 2)    cout << "top " << q.top() << endl;
            ans += q.top();
            q.pop();
        }
        printf("%d\n", ans);
    }
    return 0;
}

Problem H  UVA 12384

   先预处理出素数,然后扫一遍,每个数都往前查找,寻找第一个大于x的数的位置,由于s[i]表示以a[i]为尾的连续非降序列个数,所以如果x>=a[i],指针可以直接跳s[i]个单元,

当然这种方法算比较渣的。。吗?

  还有一种思路是:单调队列。。。我没听懂。。。

  再一种思路:线段树!

  我们可以把a[i]作为点的坐标,i作为该点的值p[a[i]] = i,计算s[i]的时候,需要往前寻找第一个a[j]>a[i],即!查找a[j]<a[i]的最大的j!,可以转化为线段树查找最大值

线段1~a[i]-1的最大值就是要找的j...

  理解了好久T T

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>

using namespace std;

const int maxn = 100000+50;

int T, n, m, tot;
int prime[maxn*10*5], x[maxn], s[maxn];
long long sum;

bool isprime(int x)
{
    if(x == 2)    return true;
    if(x % 2 == 0)    return false;
    for(int i = 3, t = sqrt(x); i <= t; i += 2)
        if(x % i == 0)    return false;
    return true;
}

int main()
{
#ifdef LOCAL
    freopen("H.in", "r", stdin);
#endif
    scanf("%d", &T);
    for(int i = 2, t = maxn*2*10; i < t; i++)
        if(isprime(i))    prime[++tot] = i;
//    cout << tot << endl;
    while(T--) {
        scanf("%d%d", &n, &m);
        sum = 0;
        memset(s, 0, sizeof(s));
        for(int i = 1; i <= n; i++) {
            if(i == 1)    s[i] = 1;
            else if(prime[i] %m >= prime[i-1] %m) {
                int j = i-s[i-1];    s[i] = s[i-1];
                while(j >= 1 && prime[i] % m >= prime[j] % m) {
                    s[i] += s[j];
                    j -= s[j];
                }
/*
                s[i] = s[i-1];
                int j = i-s[i-1]-1, cnt = 1;
                while(j >= 1 && prime[i] % m >= prime[j] % m) {
                    cnt++;
                    j--;
                }
                s[i] += cnt;
*/
            }
            else    s[i] = 1;
            sum += s[i];
            //cout << s[i];
        }
        //cout << sum << endl;
        printf("%lld\n", sum % m);
    }
    return 0;
}

顺带练习一发筛法,后面那个break不懂哟

void getPrime()
{
    int maxm = maxn*40;
    for(int i = 2; i <= maxm; i++) {
        if(!flag[i])    prime[++tot] = i;
        for(int j = 1; j <= tot && i*prime[j] <= maxm; j++) {
            flag[i*prime[j]] = true;
            if(i % prime[j] == 0)    break;
        }
    }
}

Problem I  UVA 12385

  正规做法 DP

  容易注意到需要考虑每一个字符上一次出现的位置,记为last[c[i]],显然a-a-a比a---a更优

  f[c]表示以c为尾巴的最长序列  f[c] = max(f[c-1], f[last[c]]+1), last[c]不存在的话,f[c] = 0,看了半天题解才懂。。懒得写了。。。

  傻叉瞎搞法:

  扫一遍把所有的线段跑出来,当然不包括a-a-a这种,就a-a的,然后sort下贪心。。。

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

using namespace std;

const int maxn = 100000+50;

struct interval{
    int x1, x2;

    void set(int x1, int x2) {
        this->x1 = x1;    this->x2 = x2;
    }

}seg[maxn];

int T, n, tot;
int lastn[maxn];

int cmp(const interval &a, const interval &b)
{
    if(a.x2 == b.x2)
        return a.x1 > b.x1;
    else    return a.x2 > b.x2;
}

int main()
{
#ifdef LOCAL
    freopen("I.in", "r", stdin);
#endif
    scanf("%d", &T);
    while(T--) {
        memset(lastn, -1, sizeof(lastn));
        tot = 0;
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) {
            int x;
            scanf("%d", &x);
            if(lastn[x] != -1) {
//                cout << x << ‘ ‘ << lastn[x] << ‘ ‘ << i << endl;
                seg[tot].set(lastn[x], i);
                tot++;
            }
            lastn[x] = i;
        }
        sort(seg, seg+tot, cmp);
        int left = maxn*2;
        long long ans = 0;
        for(int i  = 0; i < tot; i++) {
            if(seg[i].x2 <= left) {
                ans++;
                left = seg[i].x1;
            }
            else if(seg[i]. x1 > left) left = seg[i].x1;
        }
        printf("%lld\n", ans);
    }
    return 0;
}

UESTC 2014 Summer Training #7 Div.2

时间: 2024-10-17 12:24:48

UESTC 2014 Summer Training #7 Div.2的相关文章

UESTC 2014 Summer Training #18 Div.2

A.UVALive 6661 题意从1~N中选k个数,和为s的方案数 第一眼搜索,估计错状态量,又去yydp...浪费大量时间 数据很小的,状态数都不会超过2^N...直接dfs就过了 //state二进制表示选取的数 #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; const int maxn = 200; in

UESTC 2014 Summer Training #3 Div.2

(更新中) A:ZOJ 3611 BFS+状态压缩 [题意]:给定一张n*m的图,图上每个点有如下情况:L,R,D,U:代表在该点上只能往它已经给定的方向前进.#,W:不能走到该点.$:走到该点,可以花两分钟得到一分值,然后可以从该点向任意方向走.0:走到该点后可以向任意方向走.然后给你起点和终点坐标,问是否能从起点走到终点,如果能,求出可获得的最大分值以及与之对应达到该最大分值所需的的最小时间花 费.(其中起点和终点坐标可以相同)[知识点]:BFS+状态压缩[题解]:我觉得超级棒的题!真心感觉

UESTC 2014 Summer Training #16 Div.2

虽然被刷了还是要继续战斗下去嗯...就是基础不好,难度相对较大 A.SPOJ AMR10A 点是顺时针给出的,可以在图上画画(脑补也行),连线x-a,x-b(x为选定的一个点,比如第一个点),就把所求面积分成了四部分,a-b左边部分是较容易求出来的, 三角形面积是直接可求,另外两个多边形面积是可以预处理出来的(多个三角形面积和) 反正我是沒想出來...看題解也理解半天,多邊形面積转化为三角形面积和 嗯嗯 #include <iostream> #include <cstdio> #

UESTC 2014 Summer Training #5 Div.2

持续更新中 B:URAL 1874 三分搜索 [题意]: 给出两条边a,b以及一个墙角,求由这两条边和墙角所构成的四边形的最大面积.[知识点]: 三分搜索[题解]: 将四边形分为两个三角形,其中一个由a,b,以及第三条同时作为墙角斜边的边c,另一个三角形即为墙角与边c构成 则墙角所在的三角形的最大面积为c*c/4,abc变所在的三角形的面积可用海伦公式求出,然后主要是用三分来求这个c,得到c后就可以得到最佳答案了. 还有一种现成的结论...[代码1]: 1 #include <map> 2 #

UESTC 2014 Summer Training #6 Div.2

又是只过两水题,不过状态有些回升,也是差点一血. Problem A SPOJ AMR11A 显然的dp?就一抖就想到尝试从(R,C)推到(1,1),正着推的话,只能检查某一种解可不可行(就有人想出了二分+DP的神奇方法..刚卡过..不过上界是把所有龙加起来..不闲麻烦的话..可以按照贪心的方法先跑一个上界,就每一步选当前最优) 倒着推,龙的话,就加上,药水的话,减去?不够,和1取最大值:某一个点的f[i][j]就是两种策略取最小值: f[i][j] = min{ max(1, f[i+1][j

UESTC 2014 Summer Training #10 Div.2

B.Race to 1 UVA 11762 第一次接触概率dp,完全没想到是dp...没想到能递推出来0 0 首先需要知道 总的期望=每件事的期望×每件事发生的概率 然后可以根据这个来写递推公式,也是dp? 假设不小于x的质数有m个,x的质因子有n个(种 更确切),那么在求X的期望时,可以考虑由他能转移过去的状态转移,X,X/pi p是x的质因子 所以不难得出E(x) = 1+(m-n)/mE(X)+1/msigmaE(X/pi) 注意会加1,因为X转移到后面的情况,就多了一步 //Update

UESTC 2014 Summer Training #11 Div.2

E - Prototype ZOJ 3235 把(d2,0)代入第二个方程可以得到一个方程:经过(d2,0)的抛物线的起点的方程 再把它和第一个方程联立,就能解出两条抛物线的交点,再验算:是否在0~d2范围内,是否在x=d1时会撞到building2 注意可能不需要滑行就能到达(d2,0),先特殊处理这种情况 一开始傻逼理解错题意,后来一直修改又去考虑了不会出现的情况,例如A=0,delta<0,最后才发现了我忘记打sqrt了!!! 这场比赛题略难...就不会做 当时还是比较慌,很怕过不了题,加

UESTC 2014 Summer Training #19

A.UVALive 6161 去迟了,队友已经开始写了,应该是个水题,贴个队友代码 #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<map> #include<set> #include<vector> #include<algorithm> #inclu

UESTC 2016 Summer Training #1 Div.2

最近意志力好飘摇..不知道坚不坚持得下去.. 这么弱还瞎纠结...可以滚了.. 水题都不会做.. LCS (A) 水 LCS (B) 没有看题 Gym 100989C 水 1D Cafeteria (B) 不会捉 Gym 100989E 水 Gym 100989F 水 Mission in Amman (B) 没看题 Queue (A) 感觉题意理解得有问题啊 1 #include <iostream> 2 #include <cstdio> 3 #include <cstr