首页 > 代码库 > UESTC 2014 Summer Training #7 Div.2

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;}