天天快乐编程2020年OI集训队 训练9题解

本次主要是分治以及最短路。一定要注意赛后的补题,把自己会的题做完了,赛后不管了,那么这场训练毫无意义,要去积极尝试自己不会的,不懂的问老师。

1.4839: 麦森数

NOIP2003普及组T1
2p与2p-1有着相同的位数,2的p次方末位一定不为0,减1位数不会改变,那我们可以直接求2^p的位数,位数为log10(2*p)+1
然后就是求后500位了,裸的高精度快速幂
快速幂也是分治思想,把它和二分联系起来

#include<bits/stdc++.h>
using namespace std;
int a[510]={2};
#define M 500
void mul()
{
    for(int i=0;i<M;i++)
        a[i]*=2;
    for(int i=0;i<M;i++)
      a[i+1]+=a[i]/10,a[i]%=10;
}
void square()
{
    int b[510]={0};
    for(int i=0;i<M;i++)
        for(int j=0;j<M;j++)
            if(i+j<M)
                b[i+j]+=a[i]*a[j];
    for(int i=0;i<M;i++)
      b[i+1]+=b[i]/10,b[i]%=10;
    for(int i=0;i<M;i++)
        a[i]=b[i];

}
void quickmi(int x)
{
    if(x/2>1) quickmi(x/2);
    square();
    if(x%2) mul();
}
int main()
{
    int p;
    scanf("%d",&p);
    printf("%d\n",(int)(log10(2.0)*p)+1);
    quickmi(p);
    a[0]--;
    for(int i=M-1;i>=0;i--)
        printf("%d",a[i]);
}

2.6020: 扫雷游戏

NOIP2015 普及组T2
这个题目不需要用到分治,我们直接直接按照雷进行循环,模拟就行了
注意下标不要越界啊

#include<bits/stdc++.h>
using namespace std;
//有雷为1,无雷为0 
bool a[105][105];
int main()
{
    int n,m;
    char tmp;
    cin>>n>>m; 
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++) 
        {
            cin>>tmp;
            //如果是地雷就将这个点设为1
            if(tmp=='*') a[i][j]=1; 
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            if(a[i][j]==1)
            {
                printf("*"); //如果是地雷不用输出数字 
            }
            else
            {
                printf("%d",a[i+1][j+1]+a[i+1][j-1]+a[i+1][j]+a[i][j+1]+a[i][j-1]+a[i-1][j+1]+a[i-1][j]+a[i-1][j-1]);
            }
        }
        printf("\n");
    }
    return 0;
}

3.6272: 龙虎斗

NOIP2018 普及组T2
给出n个数,再给出一个m位置,定义a和b,(m不属于a或b):a为1m-1个数每个数用这个数到m的距离乘这个数的积之和,b则为m+1n中再在这n个每个数用这个数到m的距离乘这个数的积之和,然后在其中一个位置(p1)加入了一个数(s1),现在再给你一个数(s2),求这个数加在哪一个位置能让a和b的差最小(加上去这个数的计算方法和之前相同)
我们可以枚举举1~m-1(即a区间)内的位置去找最小值,当然记得开ll,十年OI一场空,不开long long见祖宗。

#include <bits/stdc++.h>
using namespace std;
long long a[100005], s1, s2, sum2, sum1, minn;
int m, n, p1, ans;
int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> m >> p1 >> s1 >> s2;
    a[p1] += s1;
    for (int i = 1; i < m; i++)
        sum1 += a[i] * (m - i);
    for (int i = m + 1; i <= n; i++)
        sum2 += a[i] * (i - m);
    minn = abs(sum1 - sum2);
    ans = m;
    for (int i = 1; i < m; i++)
        if (abs(sum1 + s2 * (m - i) - sum2) < minn)
        {
            minn = abs(sum1 + s2 * (m - i) - sum2);
            ans = i;
        }
    for (int i = m + 1; i <= n; i++)
        if (abs(sum2 + s2 * (i - m) - sum1) < minn)
        {
            minn = abs(sum2 + s2 * (i - m) - sum1);
            ans = i;
        }
    cout << ans;
}

4.4866: 瑞士轮

NOIP2011普及组T3
这个题有一定难度
每组比赛的胜者:赛前,总分是按降序排的;获胜后都得1分,仍是降序;
每组比赛的负者:赛前,总分是按降序排的;不得分,仍是降序。
先按初始分数排序,然后按分数高低两人一组比赛;
胜者入队A,负者入队B。这样A、B自身仍是有序的;只需进行合并操作即可。
这不就是归并排序吗?说实话,这个题确实高。

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct point
{
    int num, id, w;
    bool operator<(const point &tmp) const
    {
        if (num != tmp.num)
            return num > tmp.num;
        return id < tmp.id;
    }
} a[N >> 1], b[N >> 1], tmp[N];
int n, r, q;
void merge()
{
    int i = 1, j = 1, k = 1;
    while (i <= n && j <= n)
    {
        if (a[i].num > b[j].num || a[i].num == b[j].num && a[i].id < b[j].id)
            tmp[k++] = a[i++];
        else
            tmp[k++] = b[j++];
    }
    while (i <= n)
        tmp[k++] = a[i++];
    while (j <= n)
        tmp[k++] = b[j++];
}
int main()
{
    scanf("%d%d%d", &n, &r, &q);
    for (int i = 1; i <= 2 * n; i++)
        scanf("%d", &tmp[i].num), tmp[i].id = i;
    for (int i = 1; i <= 2 * n; i++)
        scanf("%d", &tmp[i].w);
    sort(tmp + 1, tmp + 2 * n + 1);
    for (int t = 1; t <= r; t++)
    {
        int cnt = 1;
        for (int i = 1; i < 2 * n; i += 2)
        {
            if (tmp[i].w > tmp[i + 1].w)
                a[cnt] = tmp[i], b[cnt] = tmp[i + 1], a[cnt++].num++;
            else
                a[cnt] = tmp[i + 1], b[cnt] = tmp[i], a[cnt++].num++;
        }
        merge();
    }
    printf("%d\n", tmp[q].id);
    return 0;
}

5.5914: 铺设道路

NOIP2018提高组Day1T1
它就是一个贪心。
题目里给的样例是4,3,2,5,3,5;
可以选择一个区间进行“填坑”操作;
所以我们的贪心策略是:
若a[i]>a[i-1],计数器sum+=a[i]-a[i-1];
那么为什么这样贪心是对的呢?
贪心证明
假设现在有一个坑,但旁边又有一个坑。
你肯定会选择把两个同时减1;
那么小的坑肯定会被大的坑“带着”填掉。
大的坑也会减少a[i]-a[i-1]的深度,可以说是“免费的”;
所以这样贪心是对的;

#include <bits/stdc++.h>
using namespace std;
int a[100001];

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i)
    {
        scanf("%d", &a[i]);
    }
    int sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        if (a[i] > a[i - 1])
        {
            sum += a[i] - a[i - 1];
        }
    }
    printf("%d\n", sum);
}

6.4811: 最优贸易

NOIP2009提高组T2
可以考虑一下单源最短路径。 假设我们是阿龙的话,当然会在路径上选择最便宜的地方买物品,最贵的地方售出物品。
如果对Dijkstra的更新方式进行修改,就可以求出从源点到每一个点购入的最便宜价格。
但是怎么搞呢,两次Dijkstra,再枚举ans=max(售出价格[i]-购入价格[i])即可求出答案。当然第二次Dijkstra要求进行在反图上

#include <bits/stdc++.h>
using namespace std;
const int M = 500010;
const int N = 100010;
int dist1[N], dist2[N], vis1[N], vis2[N];
vector<int> e1[N], e2[N];
struct pt1
{
    int u, dis;
    bool operator>(const pt1 &t) const { return dis > t.dis; }
} p1[N];
struct pt2
{
    int u, dis;
    bool operator<(const pt2 &t) const { return dis < t.dis; }
} p2[N];
priority_queue<pt1, vector<pt1>, greater<pt1>> q1;
priority_queue<pt2, vector<pt2>, less<pt2>> q2;
int n, m, money[N];
void dj1()
{
    for (int i = 1; i <= n; i++)
    {
        dist1[i] = money[i];
    }
    q1.push((pt1){1, dist1[1]});
    while (!q1.empty())
    {
        int u = q1.top().u;
        q1.pop();
        if (vis1[u])
            continue;
        vis1[u] = 1;
        for (int i = 0; i < e1[u].size(); i++)
        {
            int v = e1[u][i];
            if (dist1[v] > dist1[u])
            {
                dist1[v] = dist1[u];
                q1.push((pt1){v, dist1[v]});
            }
            if (vis1[v] == 0)
                q1.push((pt1){v, dist1[v]});
        }
    }
}
void dj2()
{
    for (int i = 1; i <= n; i++)
        dist2[i] = money[i];
    q2.push((pt2){n, dist2[n]});
    while (!q2.empty())
    {
        int u = q2.top().u;
        q2.pop();
        if (vis2[u])
            continue;
        vis2[u] = 1;
        for (int i = 0; i < e2[u].size(); i++)
        {
            int v = e2[u][i];
            if (dist2[v] < dist2[u])
            {
                dist2[v] = dist2[u];
                q2.push((pt2){v, dist2[v]});
            }
            if (vis2[v] == 0)
                q2.push((pt2){v, dist2[v]});
        }
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> money[i];
    for (int i = 1; i <= m; i++)
    {
        cin >> u >> v >> op;
        if (op == 1)
            e1[u].push_back(v), e2[v].push_back(u);
        else
        {
            e1[u].push_back(v);
            e2[v].push_back(u);
            e1[v].push_back(v);
            e2[u].push_back(v);
        }
    }
    dj1();
    dj2();
    int ans = 0;
    for (int i = 1; i <= n; i++)
    {
        if (vis1[i] && vis2[i])
            ans = max(ans, dist2[i] - dist1[i]);
    }
    printf("%d", ans);
    return 0;
}

7.4804: 树网的核

NOIP2007提高组T4
在直径上尺取,计算每条路径的偏心距
1、 通过Floyd算法求出顶点对之间的最短路径,并记录下具体的最短路径。
2、 标记所有在直径上的点。
3、 枚举路径的起点,进行DFS穷举路径的终点。
4、 对于一条确定下来的路径,通过直接模拟的方法得出偏心矩。

#include<bits/stdc++.h>
using namespace std;

const int maxn=305;
int G[maxn][maxn];
int main()
{
    int n,s;
    scanf("%d%d",&n,&s);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            if(i==j)G[i][j]=0;
            else G[i][j]=0x3f3f3f3f;
    for(int i=1,u,v,w;i<n;i++)
    {
        scanf("%d%d%d",&u,&v,&w);
        G[u][v]=G[v][u]=min(G[u][v],w);
    }
    for(int k=1;k<=n;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                G[i][j]=min(G[i][j],G[i][k]+G[k][j]);
    int ans=1e9;
    for(int i=1;i<=n;i++)
        for(int j=i;j<=n;j++)
        {
            if(G[i][j]>s)continue;
            int maxx=0;
            for(int k=1;k<=n;k++)
                maxx=max(maxx,(G[i][k]+G[j][k]-G[i][j])/2);
            ans=min(ans,maxx);
        }
    printf("%d\n",ans);
    return 0;
}
posted @ 2020-10-17 10:04  暴力都不会的蒟蒻  阅读(188)  评论(0编辑  收藏  举报