SDUT 2020 Summer Team Contest 2nd(for 19) BDJ题解

Clone from Regional Olympiad of Student St Petersburg, October 24, 2015

题目pdf版本

前言

主要整理了觉得有意思的题以及赛后补的题,VJ的训练赛难度确实没有多校赛高,下午的体验总体比自闭多校赛好很多了。
这次的题要用文件形式输出,前前后后因为这个整队wa了有四五发(离谱),以后细节还是要多注意。另外还有一个就是英文题面读题慢,读不懂,读错题的问题还是蛮严重的,英语阅读能力还有待加强。组队赛和单枪匹马感觉还是很不一样的,有人一起讨论动力也会比个人赛高很多,每个人都有不同的思路,大家一起头脑风暴的感觉还是挺好的。目前主要瓶颈就是英语水平和算法水平,继续努力吧!

Problem B. Black and White

  • 题意:输入b 和 w,输出一个矩形图案,矩形图案由白色块(‘@’表示)和黑色块(‘.’表示)组成,要求白色四连通子块有w个,黑色四连通子块有b个。

  • 思路:我们可以先判断黑色多还是白色多,然后用多的包围少的就行,具体包围方法如下:

    @@@
    @。@
    @@@
    @。@
    @@@
    。。。
    @@@
    。。。
    @@@
    可以发现这种形式的图形可以满足任何需求(最上面’日’字形的包围算一个连通块,黑白差多少就用最上面这种图形包围多少个,剩下相同的就间隔输出,具体看代码,好懂!)

  • AC代码:

#include 

#include 
using namespace std;

int main() {
    freopen("black.in", "r", stdin);
    freopen("black.out", "w", stdout);
    int n, m, i, t;
    scanf("%d %d", &n, &m);
    char c = '@', d = '.';
    if (n > m) {
       swap(n, m);
       swap(c, d);
    }
    cout << 2*m << ' ' << 3 << endl;
    for (i = 0; i < (m - n); i++) 
        cout << c << c << c << endl << c << d << c << endl;
    for (i = 0; i < n; i++)
        cout << c << c << c << endl << d << d << d << endl;
    return 0;
}

Problem D. Distribution in Metagonia

  • 题意: t组数据,每组输入一个n,把n拆成若干个数(可以就一个),要求每个数之间不能相互被整除且最小质因数只能是 2 或 3。

  • 思路:
    1.首先我们一定可以把n表示成 a·2x3y 的形式,a为不能被2和3整除的数。 然后我们把原问题就转换成把a拆开的小问题了,因为只要a能拆成几个不能相互被整除的数,那么他们分别乘上2x3y也是相互不能被整除的,满足题目要求。
    2.那么我们接下来就拆a,分析可知,a是奇数(a不能被2整除),有两种情况:
    如果a=1,那答案就是n。
    如果a不等于1,那么我们就拆a,方法是找到一个最大的x使得 xp,为什么会这样想呢,因为拆分最简单的情况就是拆成3的幂或者2的幂,如果我们拆成x+y,那么y一定是偶数,那么x一定不是y的倍数(x为奇数,y为偶数),同时因为x为3的最高次幂,所以y一定也不是x的倍数(反证法:如果y是x的倍数,最小2倍,那么y能被3整除,则x不是3的最高次幂,矛盾),所以这种分法一定满足题目要求(相互不能被整除)。至此,我们可以把n表示成(x+y)·2x3y,但是y不一定能刚好表示成2x3y这种形式,所以还得继续用上面的方法:先除去2和3的幂次,再减去3的最高次幂的方法去拆,可得:n=(x+2x23y2(k+z))·2x13y1,(k+z=y,∃p 使得k= 3p,最大的kx23y2)k和(2x23y2)z也不能相互被整除(因为y为偶数,所以2x23y2中至少有个2)故,这种不断取最高三次+偶数的方法一定都能满足条件,并且一定能拆完(因为是奇数+偶数的形式)

  • AC代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define LL long long
using namespace std;
const int maxn = 1e5+10;

LL a[50];
int tot;

void cal(LL n, LL m) {
    while (n % 2 == 0) m *=2, n/=2; 
    while (n % 3 == 0) m *=3, n/=3;
    if (n == 1) {
        a[tot ++] = m;
        return;
    }
    LL x = 3;
    while (x * 3 < n) x *=3;
    a[tot ++] = x*m;
    cal(n-x, m);
}

int main(int argc, char const *argv[]) {
    freopen("distribution.in", "r", stdin);
    freopen("distribution.out", "w", stdout);
    int t;
    cin >> t;
    while (t--) {
        LL n;
        cin >> n;
        tot = 0;
        cal(n, 1);
        cout << tot << endl;
        cout << a[0];
        for (int i=1; i<tot;i++) {
            cout <<' '<< a[i]; 
        }
        cout << endl;
    }
    return 0;
}

Problem J. Journey to the “The World’s Start”

  • 题意:输入一行n和t,表示有编号为1-n的地铁站台,需要在时间t内从1号站台乘坐到n号站台。第二行输入n-1个数pi表示第i种车票的价格,其中第i种车票可以从当前站台now乘坐到now±i的站台。第三行有n-2个数字依次表示2-n-1号站台下车再上车所需的时间,1号和n号站台上下车不花时间,地铁的速度是每经过一个站台花费单位1的时间。题目要求的是购买一张车票,要求在规定时间内从1-n站并且票价最低。
  • 思路:由于只买一张车票,那么最直观的想法就是先求出每张车票从1-n所花费的最少时间是多少,然后在满足时间要求的车票里选择票价最低的就行了。这里主要有两个步骤:1.求每张车票的最短时间。2.找到满足要求的车票。
    1.每张车票的最短时间,用dp[i]记录到站点i的最短时间,易得状态转移方程:dp[i]=min(dp(i-j))+v[i] 1<=j<=i-k (j取遍1-i-k的所有数)但是这样我们要计算n-1张车票,每张车票要遍历n个站台,每个站台还要往前遍历k个长度,时间复杂度为O(n3) 不超时我和你姓。那么怎么办?观察状态转移方程我们可以发现最佳答案存在于dp数组的下标区间k~i-1中,所以我们可以用一个单调队列维护这个区间来找区间内的最小值,这样计算所有车票的最小花费时间的时间复杂度为O(n)
    2.我们能发现如果 坐n站的车票,能够在t时间内到达 。那么 坐n+1站的车票,也能够在t时间内到达。这样我们就可以二分查找所需时间小于等于t-(n-1)的车票中能坐站数最少的车票就好了,即规定时间内能够到达的车票分界线,最后只需遍历分界线右边所有车票找到所需票价最小的就行,这一步骤的时间复杂度为O(logn),加上计算最小时间花费,总时间复杂度为O(nlogn)
  • AC代码:
// dp+单调队列+二分答案
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define LL long long
using namespace std;
const int maxn = 1e5 + 10, inf = 0x3f3f3f3f;

int dp[maxn], p[maxn], v[maxn], n, m;
struct node {
    int pos, val;
} q[maxn];

int check(int k) {
    memset(dp, inf, sizeof(dp));
    int head = 1, tail = 0;
    for (int i = 1; i <= k; i++) {
        dp[i] = v[i];
        while (head <= tail && q[tail].val >= dp[i]) tail--;
        q[++tail].val = dp[i];
        q[tail].pos = i;
    }
    for (int i = k; i <= n; i++) {
        while (i - q[head].pos > k) head++;
        dp[i] = min(dp[i], dp[q[head].pos] + v[i]);
        while (head <= tail && q[tail].val >= dp[i]) tail--;
        q[++tail].val = dp[i];
        q[tail].pos = i;
    }
    return dp[n];
}

int main(int argc, char const *argv[]) {
    freopen("journey.in", "r", stdin);
    freopen("journey.out", "w", stdout);
    cin >> n >> m;
    for (int i = 1; i <= n - 1; i++) scanf("%d", &p[i]);
    for (int i = 2; i <= n - 1; i++) scanf("%d", &v[i]);
    v[1] = v[n] = 0;
    int l = 1, r = n - 1;
    m = m - (n - 1);
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid) <= m) {
            r = mid - 1;
        } else
            l = mid + 1;
    }
    int ans = inf;
    for (int i = l; i <= n - 1; i++) {
        ans = min(ans, p[i]);
    }
    printf("%d\n", ans);
    return 0;
}

你可能感兴趣的:(ACM秃头历程)