集训 08/12题解

08/12:
前几题都是codeforces #Pi 的div2的题。不过那次打的太差,只看了两题。。
链接:http://codeforces.com/contest/567
A:
题意:找出一个公比为k长度为3的等比数列,由 b , bk , bk2 组成。 找出最多存在几个这样的数列,数都可以重复使用。但是保证这个数列一定是按照增的顺序出现的,出现的顺序不是增的也不行的。
例如 2 4 6 可以 4 2 6 不行。
思路:
将每个数都假定为数列中中间的那一个数bk。因为假定为第一个的话不好找到后面的,每次查找如果直接遍历肯定超时,假定为后一个的话不好保证他的顺序一定是增的。
假定当前数 i 为中间那个数以后,判断是否能整除k 如果不能,就不可能是。如果可以就将 i/k 在这之前出现的次数乘上 i*k在这后面出现的次数。
对于出现的次数可以在输入的时候记下总共出现的次数。
在遍历的时候记下在之前出现的次数,这样的话在后面出现的次数就可以用总共出现的次数减掉前面出现的。
这样要特判:如果i = i/k = i*k的话要减掉当前这个,不要多加。所以就是i = 0 或者 k = 1的时候。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
#define M 1000009
typedef long long ll;
ll a[M];
ll n,k;
int main()
{
    while(scanf("%I64d %I64d",&n,&k)==2)
    {
        ll ans = 0;
        map<ll,ll> pre,total;
        for(int i = 0;i < n;i++)
        {
            scanf("%I64d",&a[i]);
            total[a[i]]++;
        }
        pre[a[0]]++;
        for(int i = 1;i < n-1;i++)
        {
            ll t = a[i];
            int temp = 0;
            if(k == 1 || t == 0)  //特判
            {
                ans += pre[t]*(total[t]-pre[t]-1);
            }
            else if(t%k == 0)
            ans += pre[t/k]*(total[t*k]-pre[t*k]);
            pre[t]++;
        }
        printf("%I64d\n",ans);
    }
    return 0;
}


/* 3 110000 1 110000 -784901888 5 2 0 0 0 0 0 */

也可以写成这样更优雅一点,不用特判,就是在读入的时候记下这个位置的前面的有几个

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <map>
using namespace std;
#define M 1000009
typedef long long ll;
ll a[M];
ll n,k;
int main()
{
    while(scanf("%I64d %I64d",&n,&k)==2)
    {
        ll ans = 0;
        map<ll,ll> pre,total,fk;
        for(int i = 0;i < n;i++)
        {
            scanf("%I64d",&a[i]);
            if(a[i] % k == 0)
            {
                pre[i] = total[a[i]/k];
            }
            total[a[i]]++;  //此时再加保证自身也包含在前面中
            fk[i] = total[a[i]*k]; // 前面出现过的三阶数的个数。
        }
        for(int i = 1;i < n-1;i++)
        {
            ll t = a[i];
            int temp = 0;
            ans += pre[i]*(total[t*k]-fk[i]);
        }
        printf("%I64d\n",ans);
    }
    return 0;
}

B:
题意:判断图书馆至少能进容纳几个人。在这个系统启动之前可能就有人在图书馆里面了,系统关闭的时候也可能有人还在。
用一个变量存正常登陆的,另一个存答案如果是之前就进来的(就是是-号但之前没登陆过),那么就直接答案+1,如果是-号,但登陆过就把正常登陆的–。如果+号就++,比较这两个哪个大,最后就是答案。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1000009
int n;
int vis[M];
int main()
{
    while(scanf("%d",&n)==1)
    {
        getchar();
        int ans = 0;
        int temp = 0;
        for(int i = 0;i < n;i++)
        {
            char c = getchar();
            int a;
            scanf("%d",&a);
            if(c == '-')
            {
                if(!vis[a])
                {
                    ans++;
                }
                else temp--;
            }
            if(c == '+')
            {
                temp++;
                vis[a] = 1;
            }
            ans = max(ans,temp);
            getchar();
        }
        printf("%d\n",ans);
    }
    return 0;
}

D:
题意:找出几步之后,他一定是在骗人。
思路:先确定整个空间最多能放几艘船,然后用一个集合,插入的要攻击的点之前先找到比他大的,和比他小的。然后插进去。算出能放的船数减少了多少,如果当前能放的船数比所要放的小,就是在骗人了。
sum=sum(t1t2)/(a+1)+(t1temp)/(a+1)+(tempt2)/(a+1);

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
#define M 1000009
int vis[M];
int n,k,a;
int m;
int main()
{
    while(scanf("%d %d %d",&n,&k,&a)==3)
    {
        set<int> s;
        scanf("%d",&m);
        int ok = 0;
        s.insert(0);
        s.insert(n+1);
        int ans = -1;
        int sum = (n+1)/(a+1); //因为不能相邻
        for(int i = 1;i <= m;i++)
        {
            int temp;
            scanf("%d",&temp);
            if(ok) continue;
            set<int>::iterator it;  //注意迭代器的使用。。好久没用都有点不懂了。。
            it = s.upper_bound(temp);
            int t1 = *it;
            it--;
            int t2 = *it;
            s.insert(temp);
            sum = sum - (t1-t2)/(a+1) + (t1-temp)/(a+1) + (temp-t2)/(a+1);  //算出现在能放的船数。
            if(sum < k)
            {
                ans = i;
                ok = 1;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

/* 6 1 5 1 1 */

E:水,就是要注意给定的就是上升的数列了,不要麻烦了做。。。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1000009
int a[M];
typedef struct
{
    int mi;
    int ma;
}state;
state ans[M];
int n;
int main()
{
    while(scanf("%d",&n)==1)
    {
        for(int i = 0;i < n;i++)
            scanf("%d",&a[i]);
        ans[0].ma = a[n-1]-a[0];
        ans[0].mi = a[1]-a[0];
        for(int i = 1;i < n-1;i++)
        {
            ans[i].ma = max(a[n-1]-a[i],a[i]-a[0]);
            ans[i].mi = min(a[i+1]-a[i],a[i]-a[i-1]);
        }
        ans[n-1].ma = a[n-1]-a[0];
        ans[n-1].mi = a[n-1]-a[n-2];
        for(int i = 0;i < n;i++)
            printf("%d %d\n",ans[i].mi,ans[i].ma);
    }
    return 0;
}

F:
题意:给定一个A。找出满足整个式子的最大的n。
思路:
注意这题不能用二分,虽然很像二分,但是把那个方程的表打出来会发现这个不是一个单调的。
首先暴力打出素数表跟回文表,记录下对于不大于任意n的素数和回文数有几个。(要注意的是回文数有要求正数··· 第一天一直不知道错在哪,也是跪了,后来觉得表不会错了吧,看一下题目果然orzz)可以先用A = 42 找出上界。然后对于每一组都暴力找就好了。
从上界往下找,找到就弹出。之前太蠢,从下开始找,每次都要找到上界才确定最大n。

//关于数组大小的上线可以先用42/1 判断,发现大小不会超过1200000
//确定大小再跑
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1200000
#define INF 0x3f3f3f3f
bool is_prime[M];
bool is_pali[M];
int num_prime[M];
int num_pali[M];
int a[M];
int p,q;
bool judge_pali(int a)
{
    char s[100];
    sprintf(s,"%d",a);
    int l = strlen(s);
    bool ok = true;
    for(int i = 0;i < l/2;i++)
    {
        if(s[i] != s[l-1-i])
        {
            ok = false;
            break;
        }
    }
    if(!ok) return false;
    return true;
}
int main()
{
    for(int i = 1;i <= M;i++)  //fuck postive !!
    {
        if(judge_pali(i))
        {
            is_pali[i] = true;
            num_pali[i] = num_pali[i-1] +1;
        }
        else
        {
            is_pali[i] = false;
            num_pali[i] = num_pali[i-1];
        }
        is_prime[i] = true;
    }
    is_prime[1] = false;
    is_prime[0] = false;
    for(int i = 2;i <= M;i++)
    {
        if(is_prime[i])
        {
            num_prime[i] = num_prime[i-1]+1;
            for(int j = 2*i;j <= M;j += i)
                is_prime[j] = false;
        }
        else num_prime[i] = num_prime[i-1];
    }
    int ans = 0;
    while(scanf("%d %d",&p,&q)==2)
    {
        double a = (double)p/q;
        int maxx = -INF;
        int ok = 0;
        for(int i = 1;i <= M;i++)  //因为要找最大的所以用逆序从大到小找,找到直接弹出会快一点。
        {
            if(num_prime[i] - a*num_pali[i] <= 0)
            {
                maxx = i;
                ok = 1;
            }
        }
        if(!ok) printf("Palindromic tree is better than splay tree\n");
        else printf("%d\n",maxx);
    }
    /*for(int i = 0;i <= 100;i++) { printf("%d : prime = %d pali = %d\n",i,num_prime[i],num_pali[i]); printf("%d : prime - pali = %d\n",i,num_prime[i]-num_pali[i]); 不单调 会出现激增的 像-1 -1 -1 0 0 0 -1 }*/
    return 0;
}

C题留着明天写,好题,感觉涨姿势了!!!

你可能感兴趣的:(集训 08/12题解)