zoukankan      html  css  js  c++  java
  • Educational Codeforces Round 74 (Rated for Div. 2)

    A. Prime Subtraction (CF 1238 A)

    题目大意

    给定(x,y),问它们的差能否表示成质数和。

    解题思路

    任何大于(1)数都可以做质因数分解,那么只要差不是(1),都是可以的。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            LL a,b;
            read(a);
            read(b);
            if (a-b==1) puts("NO");
            else puts("YES");
        }
        return 0;
    }
    


    B. Kill 'Em All (CF 1238 B)

    题目大意

    给定(n,r),在一维数轴上,(x>0)(n)个怪物位置分别在(x_i),现可以扔一个炸弹到某位置(d),位置(d)的怪物就挂了,小于(d)的会后退(r)单位,即坐标变为(x_i-r),大于(d)的会前进(r)单位,即坐标变为(x_i+r)。若怪物坐标小于等于(0)则怪物也挂了。问最少扔多少个炸弹使得所有怪物都挂了。

    解题思路

    如果任意扔中间位置,原点远的怪物会越来越远,这时要不直接打它,要不用冲击波让它飞到小于等于(0)处这显然不是最优的。显然贪心每次打最远的怪物直到所有怪物的坐标都小于等于0或者所有怪物都挂了。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            int n;
            LL r;
            vector<LL> pos;
            bool sign[100005]={0};
            read(n);
            read(r);
            for(int u,i=1;i<=n;++i){
                read(u);
                if (!sign[u]){
                    pos.push_back(u);
                    sign[u]=true;
                }
            }
            sort(pos.begin(),pos.end(),greater<int>());
            LL cnt=0;
            for(auto i:pos){
                if (i-cnt*r<=0) break;;
                ++cnt;
            }
            write(cnt,'
    ');
        }
        return 0;
    }
    


    C. Standard Free2play (CF 1238 C)

    题目大意

    给定(h,n),表示初始高度为(h),初始有(n)块板高度分别为(h_i)是伸出来的,你需要从(h)到地面(高度为(0)),当站上一块板后,该板和下一块板的状态会发生改变,即从伸出来变成缩进去,或者从缩进去变成伸出来。你一次最多可以掉落两个高度单位。问初始最少需要改变多少块板的状态,你才能安全到达地面。

    解题思路

    手玩几个样例会发现,初始如果有连续偶数个板伸出来,这时候需要改变一个板的状态,可以改变最后一个伸出来的或者最后一个伸出来的下一块板的状态,使得这个连续板伸出来的数量为偶数。而对于中间如果有连续奇数个板伸出来,这时候也需要改变一块板的状态。问题就转换成统计连续板的奇偶性问题了。注意与地面连接的连续板的那部分不需要考虑。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            int n,h;
            read(h);
            read(n);
            int cnt=0;
            int la;
            read(la);
            int ans=0;
            for(int v,i=2;i<=n;++i){
                read(v);
                if (la-v==1) ++cnt;
                else{
                    if (cnt&1) ++ans;
                    cnt=1;
                }
                la=v;
            }
            if (la-0==1) ++cnt;
            else if (cnt&1) ++ans;
            if (h==1) ans=0;
            write(ans,'
    ');
        }
        return 0;
    }
    


    D. AB-string (CF 1238 D)

    题目大意

    给定一个长度为(n)(AB)(s),问串(s)有多少个子串是好串。一个串是好串当且仅当它的每个字母都在某个回文子串中。

    解题思路

    直接统计会爆时间,考虑每个回文串去统计对答案的贡献比较难,但发现统计不是好串的数量十分容易,我们可以容斥。
    由于串中仅包含(AB),我们考虑一段连续的长度为(l)(A)子串,记为(t),它的右边一位是(B),则(tB)不是好串,(t)减去前面若干个(A)后也不是好串,这时不是好串的数量有(l)个,如果(B)后面是(B),则(tBB)是好串。而如果后面是(A),则(tBA)还是个好串。所以不是好串的子串都是形如(AAAAAB)或者(BBBBBA),当然也有(ABBBBB)或者(BAAAAAA),所以前扫一遍后扫一遍即可得出答案。注意长度为(2)的不是好串的串重复统计的情况。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int n;
        read(n);
        char s[n];
        scanf("%s",s);
        LL ans=(LL)n*(n+1)/2;
        int cnt=1;
        for(int i=1;i<n;++i){
            if (s[i]==s[i-1]) ++cnt;
            else{
                ans-=cnt;
                cnt=1;
            }
        }
        cnt=1;
        for(int i=n-1;i>=0;--i){
            if (s[i]==s[i+1]) ++cnt;
            else{
                ans-=cnt-1;
                cnt=1;
            }
        }
        ans-=n;
        write(ans,'
    ');
        return 0;
    }
    


    E. Keyboard Purchase (CF 1238 E)

    题目大意

    给定(n,m),以及长度为(n)的仅包含字母表前(m)个的串(s),现在需要重新安排这前(m)个字母的顺序,排成一列,使得输入这个串(s)的代价最小。输入串的代价为手指需要移动的距离。

    解题思路

    考虑暴力,即枚举排列的情况分别计算,我们发现这很像数位(DP)。由于(mleq 20),我们可以状压,设(dp[k])表示我们已经排好的字母的状态下,剩下的字母排列的所有情况中,需要的最小代价。由于(k)在二进制中(1)的数量暗示了当前要填的数的位置,我们枚举当前位置(pos)要填的一个字母(x),考虑填这个字母后它所贡献的代价,即为(cnt_{xy} imes |pos_x-pos_y|),其中(cnt_{xy})表示在串(s)中,字母(xy)相邻的次数。但由于(y)的位置从我们设置的状态中无法得知,或者还没填进去。但我们把因子乘进去,我们可以分别计算两者的贡献,即填(y)的时候,我们把(-cnt_{xy} imes pos_y)放进答案,当填(x)的时候,我们再把(cnt_{xy} imes pos_x)放进答案,这样就组成了(cnt_{xy} imes (pos_x-pos_y))。所以我们填(x)后,对于已经填进去的字母(y)我们把(cnt_{xy} imes pos_x)放进答案,而没出现的,就把(-cnt_{xy} imes pos_x)放进答案。记忆化搜索即可。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    const int N=1e5+8;
    
    const LL INF=1e9+7;
    
    int n,m;
    
    char s[N];
    
    LL dp[1<<20];
    
    int cnt[27][27];
    
    void solve(int cur,int num){
        if (cur==(1<<m)-1) return;
        if (dp[cur]!=INF) return;
        for(int i=0;i<m;++i){
            if ((cur>>i)&1) continue;
            LL qwq=0;
            for(int j=0;j<m;++j){
                if (i==j) continue;
                if ((cur>>j)&1) qwq+=cnt[i][j]*num;
                else qwq-=cnt[i][j]*num;
            }
            solve(cur|(1<<i),num+1);
            dp[cur]=min(dp[cur],dp[cur|(1<<i)]+qwq);
        }
    }
    
    int main(void) {
        read(n);
        read(m);
        scanf("%s",s);
        int len=strlen(s);
        for(int i=1;i<len;++i){
            ++cnt[s[i-1]-'a'][s[i]-'a'];
            ++cnt[s[i]-'a'][s[i-1]-'a'];
        }
        for(int i=0;i<(1<<m);++i) dp[i]=INF;
        dp[(1<<m)-1]=0;
        solve(0,1);
        write(dp[0],'
    ');
        return 0;
    }
    


    F. The Maximum Subtree (CF 1238 F)

    题目大意

    给定一棵树,要求找出最大的子树,使得该子树可以通过给定的方法构造出来。给定的方法为,选择一些线段,它们分别代表一个点,如果线段之间有交点,它们点与点之间有连线,然后这些线段所形成的图是你选定的子树。

    解题思路

    我们先考虑由线段构成的树会有什么性质。
    容易发现这种树它的任意一个点所连的所有点中,最多只能有两个点有孩子。
    然后就是一个找带权的最大直径问题,这个权是某点所连接的无孩子的点的数量。

    或者可以采用树型(DP),如果我们强行规定一个根,那么根可以有两个孩子有孙子,其余的仅可以有一个孩子有孙子。(dp[u][0])表示以(u)为根的子树的最大子树,(dp[u][1])表示以(u)父亲为根,(u)子树的最大子树。
    (dp[u][0]=MAX+SEC\_MAX+deg(u)+1,dp[u][1]=MAX+deg(u)-1),其中(deg(u))表示点(u)的度,(MAX和SEC\_MAX)表示(u)的所有孩子(son_u)中,(dp[son_u][1])的最大值和次大值。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    void DFS(int u,int fa,vector<int> edge[],vector<int> &deg,int dp[][2]){
        int ma=0,sma=0;
        for(int v:edge[u]){
            if (v==fa) continue;
            DFS(v,u,edge,deg,dp);
            if (dp[v][1]>=ma){
                sma=ma;
                ma=dp[v][1];
            }
            else sma=max(dp[v][1],sma);
        }
        dp[u][0]=ma+sma+deg[u]+1;
        dp[u][1]=ma+deg[u]-1;
    }
    
    int main(void) {
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            int n;
            read(n);
            vector<int> edge[n+1],deg(n+1);
            for(int u,v,i=1;i<n;++i){
                read(u);
                read(v);
                edge[u].push_back(v);
                edge[v].push_back(u);
                ++deg[u];
                ++deg[v];
            }
            int dp[n+1][2]={0};
            int root=1;
            while(root<=n&&deg[root]==1) ++root;
            if (root>n) {puts("2"); continue;}
            DFS(root,root,edge,deg,dp);
            int ans=0;
            for(int i=1;i<=n;++i) ans=max(ans,dp[i][0]);
            write(ans,'
    ');
        }
        return 0;
    }
    


    G. Adilbek and the Watering System (CF 1238 G)

    题目大意

    浇花,水壶容量(c),每分钟消耗一个单位的水,要浇(m)分钟,初始有(c0leq c)单位的水,现有(n)个朋友,第(i)个朋友会在(t_i)分钟来,最多带(a_i)单位的水,每单位水售价(b_i)。现要求能不断浇水,直到(m)分钟。最小化花费。不可行输出(-1)

    解题思路

    其实完全可以变成汽车加油问题。
    只是油箱容量有限制,我们仿照汽车加油问题采取贪心策略。
    由于原先的汽车加油问题中油箱没有容量限制,使得我们可以在没油的时候才决定之前该在哪个加油站加油。而这里有限制,如果仍采取上述策略的话很有可能导致某处加油会爆容量。

    为了防止爆容量,我们可以在每个加油站都把油箱加满,而在使用油的时候再认为我们花费了钱去买它。当到达一个加油站时,我们对油箱里单价比当前加油站的油高的油进行替换,这样我们就时刻保证油箱里的油是可使用的最便宜的,正确性很显然的。用(map)即可实现。(map[i]=j)表示油箱里单价为(i)的油有(j)单位。

    神奇的代码
    #include <bits/stdc++.h>
    #define MIN(a,b) ((((a)<(b)?(a):(b))))
    #define MAX(a,b) ((((a)>(b)?(a):(b))))
    #define ABS(a) ((((a)>0?(a):-(a))))
    using namespace std;
    typedef long long LL;
    typedef vector<int> VI;
    typedef pair<int,int> PII;
    typedef vector<PII> VPII;
    typedef vector<LL> VL;
    typedef pair<LL,LL> PLL;
    typedef vector<PLL> VPLL;
    
    template <typename T>
    void read(T &x) {
        int s = 0, c = getchar();
        x = 0;
        while (isspace(c)) c = getchar();
        if (c == 45) s = 1, c = getchar();
        while (isdigit(c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
        if (s) x = -x;
    }
    
    template <typename T>
    void write(T x, char c = ' ') {
        int b[40], l = 0;
        if (x < 0) putchar(45), x = -x;
        while (x > 0) b[l++] = x % 10, x /= 10;
        if (!l) putchar(48);
        while (l) putchar(b[--l] | 48);
        putchar(c);
    }
    
    int main(void) {
        int kase; read(kase);
        for (int i = 1; i <= kase; i++) {
            int n,m,c,c0;
            read(n);
            read(m);
            read(c);
            read(c0);
            map<int,int> qwq;
            vector<pair<int,pair<int,int>>> f;
            for(int t,a,b,i=1;i<=n;++i){
                read(t);
                read(a);
                read(b);
                f.push_back(make_pair(t,make_pair(b,a)));
            }
            f.push_back(make_pair(0,make_pair(0,c0)));
            f.push_back(make_pair(m,make_pair(0,0)));
            sort(f.begin(),f.end(),less<pair<int,pair<int,int>>>());
            LL ans=0;
            int cur=c0;
            qwq[f[0].second.first]=f[0].second.second;
            for(int i=1;i<=n+1;++i){
                if (f[i].first>m) break;
                int tmp=f[i].first-f[i-1].first;
                if (tmp>cur){ans=-1; break;}
                while(tmp>0){
                    int qaq=min(tmp,qwq.begin()->second);
                    ans+=(LL)qaq*qwq.begin()->first;
                    qwq.begin()->second-=qaq;
                    if (qwq.begin()->second==0) qwq.erase(qwq.begin());
                    tmp-=qaq;
                    cur-=qaq;
                }
                cur+=f[i].second.second;
                qwq[f[i].second.first]+=f[i].second.second;
                while(cur>c){
                    int qaq=min(cur-c,qwq.rbegin()->second);
                    cur-=qaq;
                    qwq.rbegin()->second-=qaq;
                    auto it=qwq.end();
                    --it;
                    if (qwq.rbegin()->second==0) qwq.erase(it);
                }
            }
            write(ans,'
    ');
        }
        return 0;
    }
    


  • 相关阅读:
    Photoshop 更改图片颜色
    搜索引擎优化网页设计:最佳实践
    Cocos2d-x 精灵碰撞检測(方法一)
    JS 计算2个日期相差的天数
    《Java并发编程实战》第七章 取消与关闭 读书笔记
    LeetCode_3Sum
    Pascal Hexagrammum Mysticum 的深度探索
    HDU 5274 Dylans loves tree(LCA+dfs时间戳+成段更新 OR 树链剖分+单点更新)
    高速查询hive数据仓库表中的总条数
    cocos2d-x触摸事件优先级
  • 原文地址:https://www.cnblogs.com/Lanly/p/12253208.html
Copyright © 2011-2022 走看看