Codeforces Round #506 (Div. 3) 题解

[比赛链接]

         https://codeforces.com/contest/1029

[比赛经历]

         本想靠DIV3上点分,没想到时差没有倒过来,最后三题收场,第二天SYSTEM TEST的时候D题还被卡常了,最后只加了1rating,算是一场比较失败的比赛吧

[题解]

        A. Many Equal Substrings

        [算法]

                我们可以找到这个字符串中最长的后缀 = 前缀的长度(记为T) , 那么 , 最后的答案就是S + S[T + 1..LEN] + S[T + 1..LEN] + .... + S[T + 1,LEN](共(k - 1)个S[T + 1,LEN]) , 最长后缀 = 前缀的长度我们可以通过kmp的next数组求出 , 当然 , 由于n比较小,朴素算法也是可以实现的

                由于答案字符串的长度很短 , 我们也可以直接模拟 , 笔者在比赛时就采取了这种做法

                时间复杂度 : O(N)

       [代码]

             

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

int n,k,len;
string t,ans;

template <typename T> inline void read(T &x)
{
    int f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) { if (c == '-') f = -f; }
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}

int main() 
{
        
        cin >> n >> k;
        cin >> t;
        ans = t;
        int sum = 1 , now = 1;
        while (true)
        {
                if (sum == k) break;
                if (now == (int)ans.size()) ans += t; 
                else if (now + n - 1 >= (int)ans.size()) 
                {
                        if (now != (int)ans.size() && ans.substr(now,(int)ans.size() - now) != t.substr(0,(int)ans.size() - now)) now++;
                        else ans += t[(int)ans.size() - now]; 
                } else if (ans.substr(now,n) == t.substr(0,n)) 
                {
                        sum++;
                        now++;
                } else now++;
        }
        cout<< ans << endl;
        
        return 0;
    
}

            B. Creating the Contest

            [算法]

                   我们可以扫描一遍数组 , 当扫描到一个元素时 , 我们贪心地将该元素加入满足条件的 , 大小最大的组中 , 如果不存在满足条件的组就新建一个 , 最后的答案为最大的组的大小

                   时间复杂度 : O(N)

            [代码]

                     

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2 * 1e5 + 10;

int n,tot;
int g[MAXN],a[MAXN],sz[MAXN];

template <typename T> inline void read(T &x)
{
    int f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) { if (c == '-') f = -f; }
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}

int main() 
{
        
        read(n);
        for (int i = 1; i <= n; i++) read(a[i]);
        g[tot = 1] = a[1];
        sz[1] = 1;
        for (int i = 2; i <= n; i++)
        {
                int pos = -1;
                for (int j = 1; j <= tot; j++)
                {
                        if (a[i] <= g[j] * 2)
                        {
                                if (pos == -1 || sz[j] > sz[pos])
                                        pos = j;        
                        }
                }        
                if (pos != -1) 
                {
                        g[pos] = a[i];
                        sz[pos]++;
                } else 
                {
                        g[++tot] = a[i];
                        sz[tot] = 1;
                }
        }
        int ans = 0;
        for (int i = 1; i <= tot; i++) ans = max(ans,sz[i]);
        printf("%d
",ans);
        
        return 0;
    
}

                C. Maximal Intersection

                [算法]

                        首先 , n条线段的交集长度为[Lmax,Rmin] , 其中Lmax为最靠右的左端点,Rmin为最靠左的右端点,若Lmax > Rmin , 说明这些线段无交集 

                        发现这个性质以后 , 我们可以枚举删去哪一条线段, 在剩余线段中找到最靠右的左端点和最靠左的右端点 ,用两者的差值更新答案,具体实现时,

                        我们可以记录前缀最值和后缀最值, 当然 , STL中的multiset和线段树等数据结构也是可以完成这个任务的

                       时间复杂度 : O(N) 或 O(N log N)

               [代码]

                       

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 3e5 + 10;
const int inf = 2e9;

int n;
int l[MAXN],r[MAXN],premn[MAXN],sufmn[MAXN],premx[MAXN],sufmx[MAXN];

template <typename T> inline void read(T &x)
{
    int f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; 
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}

int main() 
{
        
        read(n);
        for (int i = 1; i <= n; i++) 
        {
                read(l[i]);    
                read(r[i]);
        }
        if (n == 1)
        {
                printf("%d
",r[1] - l[1]);
                return 0;
        }
        premx[0] = -inf;
        for (int i = 1; i <= n; i++) premx[i] = max(premx[i - 1],l[i]);
        sufmx[n + 1] = -inf;
        for (int i = n; i >= 1; i--) sufmx[i] = max(sufmx[i + 1],l[i]);
        premn[0] = inf;
        for (int i = 1; i <= n; i++) premn[i] = min(premn[i - 1],r[i]);
        sufmn[n + 1] = inf;
        for (int i = n; i >= 1; i--) sufmn[i] = min(sufmn[i + 1],r[i]);
        int ans = 0;
        for (int i = 1; i <= n; i++) ans = max(ans,min(premn[i - 1],sufmn[i + 1]) - max(premx[i - 1],sufmx[i + 1]));
        printf("%d
",ans);
        
        return 0;
    
}

                    D.Concatenated Multiples

                    [算法]

                            不难发现 , 所有符合条件的二元组(i,j)都满足 : 10^(len(j)) * a[i] + a[j] 是k的倍数 ,我们不妨开10个std :: map , 其中,第m个map记录的是10^m * a[t] ( 1 <= t <= n)对k取模为V(0 <= V < k)的数的个数 , 然后我们枚举每一个数更新答案 , 最后 , 我们还需将答案减去(i,i)这样的

                            不合法的二元组

                            时间复杂度 : O(10Nlog(N))

                    [代码] 

                            此题时限很紧 , 注意优化常数 !

                            

#include<bits/stdc++.h>
using namespace std;
#pragma GCC optimize("O2")
#pragma GCC optimize("O3")
#pragma GCC optimize("Ofast")
const int MAXN = 2 * 1e5 + 10;
typedef long long ll;

int n;
ll k,ans;
map< ll,int > mp[11];
ll a[MAXN];

template <typename T> inline void read(T &x)
{
    int f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) { if (c == '-') f = -f; }
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}
inline int len(ll n)
{
        int ret = 0;
        while (n > 0)
        {
                ret++;
                n /= 10;
        }
        return ret;
}
inline ll Pow(int a,int n)
{
        ll ret = 1;
        for (int i = 1; i <= n; i++) ret = 1ll * ret * a % k;
        return ret;
}
int main() 
{
        
        read(n); read(k);
        for (int i = 1; i <= n; i++) read(a[i]);
        for (int i = 1; i <= n; i++)
        {
                for (int j = 1; j <= 10; j++)
                {
                        mp[j][1ll * Pow(10,j) * 1ll * a[i] % k]++;
                }
        }
        for (int i = 1; i <= n; i++) if (mp[len(a[i])].count((k - a[i] % k) % k)) ans += mp[len(a[i])][(k - a[i] % k) % k];
        for (int i = 1; i <= n; i++)
        {
                if ((1ll * Pow(10,len(a[i])) * a[i] % k + 1ll * a[i]) % k == 0)
                        ans--;
        }
        printf("%I64d
",ans);
        
        return 0;
    
}

                     E. Tree with Small Distances

                     [算法]

                             首先将所有的深度大于2的点加入一个堆中 , 每次我们找到深度最大的点 , 将这个点从堆中弹出 , 然后 , 我们将这个点的父节点和1号节点连一条边 , 在堆中删除所有与该节点父节点相连的边 

                             当堆中没有元素时, 就得到了答案

                             时间复杂度 : O(NlogN)

                     [代码]

                             

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 10;
const int inf = 2e9;

int n,tot,ans;
int head[MAXN],fa[MAXN],dep[MAXN];
priority_queue< pair<int,int> > q;
bool visited[MAXN];

struct edge
{
        int to,nxt;
} e[MAXN << 1];

template <typename T> inline void read(T &x)
{
    int f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; 
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}
inline void addedge(int u,int v)
{
        tot++;
        e[tot] = (edge){v,head[u]};
        head[u] = tot;
}
inline int dfs(int u,int par)
{
        for (int i = head[u]; i; i = e[i].nxt)
        {
                int v = e[i].to;
                if (v == par) continue;
                fa[v] = u;
                dep[v] = dep[u] + 1;
                dfs(v,u);
        }
}

int main() 
{
        
        read(n);
        for (int i = 1; i < n; i++)
        {
                int u,v;
                read(u); read(v);
                addedge(u,v);
                addedge(v,u);
        }
        dfs(1,0);
        for (int i = 1; i <= n; i++)
        {
                if (dep[i] > 2)
                        q.push(make_pair(dep[i],i));
        }
        while (!q.empty())
        {
                int u = q.top().second;
                q.pop(); 
                if (visited[u]) continue;
                visited[u = fa[u]] = true;
                ans++;
                for (int i = head[u]; i; i = e[i].nxt)
                {
                        int v = e[i].to;
                        visited[v] = true;
                }
        }
        printf("%d
",ans);
        
        return 0;
    
}

                        F. Multicolored Markers

                        [算法]

                                  首先, 大长方形的边长一定是(a + b)的约数, 所以 , 我们可以从1到sqrt(a + b)枚举较小的那些因子(其中,sqrt表示开根号) , 求出(a + b)的所有约数

                                  既然我们要求至少有一种颜色的格子满足这些格子也能构成长方形 , 那么 , 我们可以同样用上述方法求出a和b的所有因子 

                                  为了方便讨论,我们假设所有红色颜色的格子能够构成长方形 

                                  然后 , 我们枚举大正方形的长d1 ,  用二分或Two Pointers求出小于d1的 , 最大的a的约数 , 如果((a + b) / d1 >= a / d2) , 我们就用(d1 + (a + b) / d1) * 2更新答案

                                  蓝色格子同理

                                  时间复杂度 : O(sqrt(a + b)) (其中 , sqrt表示开根号 )

                        [代码]

                               

#include<bits/stdc++.h>
using namespace std;
const int MAXD = 1e5 + 10;
typedef long long ll;
const ll inf = 1e18;

ll a,b,ans,lena,lenb;
ll fa[MAXD],fb[MAXD];

template <typename T> inline void read(T &x)
{
    ll f = 1; x = 0;
    char c = getchar();
    for (; !isdigit(c); c = getchar()) { if (c == '-') f = -f; }
    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}

int main() 
{
        
        read(a); read(b);
        ll tmp = a + b;
        for (ll i = 1; 1ll * i * i <= a + b; i++)
        {
                if (tmp % i == 0)
                {
                        fa[++lena] = i;
                        if (1ll * i * i != a + b) fa[++lena] = (a + b) / i;
                }
        }
        for (ll i = 1; 1ll * i * i <= a; i++)
        {
                if (a % i == 0)
                {
                        fb[++lenb] = i;
                        if (1ll * i * i != a) fb[++lenb] = a / i;
                }
        }
        sort(fb + 1,fb + lenb + 1);
        fb[++lenb] = inf;
        ans = inf;
        for (int i = 1; i <= lena; i++)
        {
                ll d1 = fa[i];
                int pos = lower_bound(fb + 1,fb + lenb + 1,d1) - fb;        
                if (fb[pos] > d1 && pos == 1) continue;
                if (fb[pos] > d1) pos--;
                ll d2 = fb[pos];
                if ((a + b) / d1 >= a / d2) ans = min(ans,2 * (d1 + (a + b) / d1));
        }
        lenb = 0;
        for (ll i = 1; 1ll * i * i <= b; i++)
        {
                if (b % i == 0)
                {
                        fb[++lenb] = i;    
                        if (1ll * i * i != b) fb[++lenb] = b / i;
                }
        }
        sort(fb + 1,fb + lenb + 1);
        fb[++lenb] = inf;
        for (int i = 1; i <= lena; i++)
        {
                ll d1 = fa[i];
                int pos = lower_bound(fb + 1,fb + lenb + 1,d1) - fb;        
                if (fb[pos] > d1 && pos == 1) continue;
                if (fb[pos] > d1) pos--;
                ll d2 = fb[pos];
                if ((a + b) / d1 >= b / d2) ans = min(ans,2 * (d1 + (a + b) / d1));
        }
        printf("%I64d
",ans);
        
        return 0;
    
}

                [总结]

                1. 这次比赛的失利不仅是因为时差的问题 , 我认为 , 更多的还是我的基本功不够扎实 , 值得反思

                2. 突然发现打比赛也是一种提高水平的好方法 , 每次比赛都能发现自己的薄弱点并订正试题 , 查漏补缺 , 以后要多参加这些比赛 , 不仅提高了能力 , 而且 , 英语水平也能得到提高

                3. 离noip2018只剩两个月了 , 在剩下的两个月中 , 我还需更加努力 , 调整好心态 , 争取最后取得理想的成绩

原文地址:https://www.cnblogs.com/evenbao/p/9538327.html