zoukankan      html  css  js  c++  java
  • ACM团队周赛题解(1)

    这次周赛题目拉了CF315和CF349两套题。

    因为我代码模板较长,便只放出关键代码部分

    #define ll long long

    #define MMT(s,a) memset(s, a, sizeof s)
    #define GO(i,a,b) for(int i = (a); i < (b); ++i)
    #define GOE(i,a,b) for(int i = (a); i <= (b); ++i)
    #define OG(i,a,b) for(int i = (a); i > (b); --i)
    #define OGE(i,a,b) for(int i = (a); i >= (b); --i)

    这是代码中的几个宏定义,需要拿代码修改这个几个就行了,若是用到代码其他定义部分,会在代码中额外添加。

    这里也只是部分题解。


    A - Cinema Line (CF-349A)

    题意很简单,就是说你是售票员,门票价格25元,有n个人来买票,钱只有25,50,100三种,最开始你没有钱,问你是否可以完成售票过程,即每个人都有足够的零钱找他,按顺序购票。

    题目思路模拟即可。

     1 int main(){
     2     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     3     int n,a,flag = 1,tot1 = 0,tot2 = 0,tot3 = 0;
     4     cin>>n;
     5     GO(i,0,n){
     6         cin>>a;
     7         if(a == 25){
     8             tot1++;
     9         }
    10         else if(a == 50){
    11             if(tot1 > 0){
    12                 tot1--;
    13                 tot2++;
    14             }
    15             else
    16                 flag = 0;
    17         }
    18         else if(a == 100){
    19             if(tot2 > 0 && tot1 > 0){
    20                 tot2--,tot1--;
    21                 tot3++;
    22             }
    23             else if(tot1 > 2){
    24                 tot1-=3;
    25                 tot3++;
    26             }
    27             else
    28                 flag = 0;
    29         }
    30     }
    31     if(flag)
    32         cout << "YES" << endl;
    33     else
    34         cout << "NO" << endl;
    35     return 0;
    36 }

     

    B - Color the Fence (CF-349B)

    题目意思就是最开始有n,然后选取第i个数就需要花费a[i],问最大能够成的数。

    题目思路:首先需要保证位数最大,所以先找到最小值min,n/min即为最大位数,然后对于每一位,从9-1往回找输出。值得注意的是,可能你选取某个数会使得位数减少,所以选取数还得判断是否会影响位数。双重循环贪心。

     1 int n,a[10] = {0};
     2 int flag = 0,ii;
     3 
     4 int main(){
     5     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     6     cin>>n;
     7     int minn = INT_MAX;
     8     GOE(i,1,9){
     9         cin>>a[i];
    10         if(a[i] < minn)
    11             minn = a[i];
    12     }
    13     if(minn > n){
    14         cout << -1 << endl;
    15         return 0;
    16     }
    17     int cnt = n / minn;
    18     OGE(i,cnt,1){
    19         OGE(j,9,1){
    20             if(n >= a[j] && (n-a[j]) / minn >= i-1){
    21                 cout << j;
    22                 n -= a[j];
    23                 break;
    24             }
    25         }
    26     }
    27     cout << endl;
    28     return 0;
    29 }

    C - Mafia(CF-349C)

    题目意思就是n个人进行Mafia的游戏,游戏规则就是选取一个人当裁判(类似狼人杀),其他n-1个人进行游戏,如果某人某局为裁判,则不计入他的游戏对局数。问要保证每个人i都玩了a[i]局游戏。

    题目思路:首先要保证最大值max = max(a[i])玩完,所以游戏至少要玩max轮,在这max轮对局中,当某个人玩成了他的a[i]局,那么剩下的max-a[i]局他就可以当裁判为其他人服务。

    所以令sum = Σ(max - a[i])。

    当sum >= max时,说明有足够的轮数可以让非最大值的人当裁判去完成最大值的max轮,这种情况只需要玩max局即可。

    当sum < max时,则需要额外的轮数让其他人为最大值的人当裁判,而每一轮有n-1个人可以当裁判。这种情况就玩(max - sum)/(n-1)+max即可。

    同理这道题也可以二分,因为每局都有n-1个人可以当裁判为剩下的一个人服务。那么则从max —— Σ(a[i])二分选取cnt*(n-1) >= sum即可。

     1 int a[100005];
     2 
     3 int main(){
     4     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     5     int n,maxx = INT_MIN;
     6     ll sum = 0;
     7     cin>>n;
     8     GO(i,0,n){
     9         cin>>a[i];
    10         maxx = max(a[i],maxx);
    11     }
    12     GO(i,0,n)
    13         sum += maxx - a[i];
    14     if(sum >= maxx)
    15         cout << maxx << endl;
    16     else{
    17         int cnt = 0;
    18         while(sum < maxx){
    19             cnt++;
    20             sum += n-1;
    21         }
    22         cout << maxx+cnt << endl;
    23     }
    24 
    25     return 0;
    26 }

    D - Apple Tree(CF-349D)

    题意就是有一颗苹果树,根节点为1,然后保证非叶子节点的值都是0(就是树枝上不会有苹果,很好理解),叶子节点的值是a[i],然后现在要使这颗树变得平衡,平衡的定义为,每个非叶子节点的所有子节点值相同(就是说某个树杈的所有树枝包含的苹果相同)。

    题目思路:对于每一个结点u,要知道它的总分支数r[u]及现在所拥有的权值和val[u],因为不同子树总分支数不一定相同,故结点u每次减少的值需要是其所有子树分支的最小公倍数,而且对于u的子树也需要保证平衡,故u点每次需减去的值 = lcm * 儿子个数,值得注意的是lcm可能会爆long long,这种情况我们可以认为这个树平衡当且仅当所有的苹果都被拿走,即全部去掉。

    那么对于任意一个节点,先计算这个节点可拿走的苹果数,再计算苹果数目的上界,贪心选取最大重量更新节点情况。

    代码还有额外的宏定义

    #define PB push_back

    const ll INFF = 0x3f3f3f3f3f3f3f3f;

    template<typename T>
    inline T gcd(T a, T b){ return b==0 ? a : gcd(b,a%b); }
    template<typename T>
    inline T lcm(T a, T b){ if(a*b > INFF) return 0; return a / gcd(a,b) * b; }

     1 const int maxn = 100005<<1;
     2 
     3 ll v[maxn],dp[maxn],dis[maxn];
     4 ll sum = 0;
     5 vector<int> Edge[maxn];
     6 bool flag;
     7 
     8 void Add(int l,int r){
     9     Edge[l].PB(r);
    10     Edge[r].PB(l);
    11 }
    12 
    13 void dfs(int st,int fa){
    14     dp[st] = v[st],dis[st] = 1;
    15     int cnt = 0;
    16     for(auto it : Edge[st]){
    17         if(it == fa)
    18             continue;
    19         cnt++;
    20         dfs(it,st);
    21         int temp = lcm(dis[st],dis[it]);
    22         if(temp)
    23             dis[st] = temp;
    24         else
    25             dis[st] = 1, flag = 0;
    26         dp[st] += dp[it];
    27     }
    28     for(auto it : Edge[st]){
    29         if(it == fa)
    30             continue;
    31         dp[st] = min(dp[st],dp[it] - dp[it]%dis[st]);
    32     }
    33     (cnt == 0) && cnt++;
    34     dp[st] *= cnt, dis[st] *= cnt;
    35 }
    36 
    37 int main(){
    38     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
    39     int n,l,r;
    40     cin>>n;
    41     GOE(i,1,n){
    42         cin>>v[i];
    43         sum += v[i];
    44     }
    45     GO(i,1,n){
    46         cin>>l>>r;
    47         Add(l,r);
    48     }
    49     flag = 1;
    50     dfs(1,0);
    51     if(!flag)
    52         cout << sum << endl;
    53     else
    54         cout << sum - dp[1] << endl;
    55 
    56     return 0;
    57 }

    F - Sereja and Bottles(CF-315A)

    题意就是n个瓶子,a[i]瓶可以打开其他的第b[i]瓶,问最后剩下多少瓶子没有打开。

    题目思路:标记模拟即可

     1 int main(){
     2     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     3     int n,cnt(0);
     4     int a[105],b[105],vis[105] = {0};
     5     cin>>n;
     6     GOE(i,1,n)
     7         cin>>a[i]>>b[i];
     8     GOE(i,1,n){
     9         GOE(j,1,n){
    10             if(i != j && a[j] == b[i])
    11                 vis[j] = 1;
    12         }
    13     }
    14     GOE(i,1,n){
    15         if(vis[i])
    16             cnt++;
    17     }
    18     cout << n - cnt << endl;
    19 
    20     return 0;
    21 }

    G - Sereja and Array(CF-315B)

    题意就是三种操作.

    1 就是把第x个元素变成v;

    2 就是把所有元素加上v;

    3 即使输出第x个元素的值;

    题目思路:1,3操作容易实现,主要是2操作,总不能遍历把每个值加上v,因为题目说了是所有值加上v,所以只需要用一个数记录v的和,输出是加上这个就行,同时需要因为有1操作,所以还需要开额外一个数组记录当某个值改变时,存取当前的v总和,输出再减去这个值即可。

     1 int n,m,temp = 0;
     2 int a[100005],dis[100005];
     3 int x,y,z;
     4 
     5 int main(){
     6     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     7     cin>>n>>m;
     8     GOE(i,1,n)
     9         cin>>a[i];
    10     GO(i,0,m){
    11         cin>>x;
    12         if(x == 1){
    13             cin>>y>>z;
    14             dis[y] = temp;
    15             a[y] = z;
    16         }
    17         else if(x == 2){
    18             cin>>y;
    19             temp += y;
    20         }
    21         else if(x == 3){
    22             cin>>y;
    23             cout << a[y] + temp - dis[y] << endl;
    24         }
    25     }
    26 
    27     return 0;
    28 }

    H - Sereja and Contest(CF-315C)

    题意就是有n个数字,当f(a[i])小于k时删除这个数,输出其位置,再从新计算。

    f(i) = Σ(a[j]*(j-1) - (n-i)*a[i]);

    题目思路:第一轮删除了a[i],那么下一轮删除的数的位置,一定时大于i的。

    这个公式可以变成(j-1)* Σ(a[j]) - (j-1)*(n-i)*a[i],可以发现前半部分就是一个前缀和,所以过程中维护n的大小动态修改。

     1 int n,k;
     2 ll a[200005];
     3 
     4 int main(){
     5     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     6     cin>>n>>k;
     7     GOE(i,1,n)
     8         cin>>a[i];
     9     ll now(0),tot(0),temp(n);
    10     GOE(i,2,n){
    11         now += a[i-1]*tot;
    12         if(now - (temp-i+(n-temp))*a[i]*(i-1-(n-temp)) < k){
    13             temp--;
    14             now -= a[i]*tot;
    15             cout << i << endl;
    16         }
    17         else
    18             tot++;
    19     }
    20     return 0;
    21 }

    I - Sereja and Periods(CF-315D)

    题目意思就是两个串分别是[a,b],[c,d],运算规则就是b个a相连接,例如[abc,2] = abcabc;

    然后问你[a,b]这个串中出现了几次[c,d]。

    题目思路:KMP找循环节。用cnt[i]记录当前开始以c串的i位置找,经过一个a串后,会经过几个c串,nxt记录当前开始以c串的i位置开始找,经过一个a串后,匹配的位置会到什么地方。每次对于a串都是从0~lena找,因为走完一个a串后,下一条又从0开始了。

    1e7,单层循环,没问题。

     1 int b,d,cnt[105] = {0},nxt[105];
     2 string a,c;
     3 
     4 int main(){
     5     ios_base::sync_with_stdio(false), cout.tie(0), cin.tie(0);
     6     cin>>b>>d>>a>>c;
     7     int lenc = c.size(),lena = a.size();
     8     GO(i,0,lenc){
     9         int temp = i;
    10         GO(j,0,lena){
    11             if(a[j] == c[temp]){
    12                 temp++;
    13                 if(temp == lenc)
    14                     cnt[i]++, temp = 0;
    15             }
    16         }
    17         nxt[i] = temp;
    18     }
    19     int j = 0;
    20     ll sum = 0;
    21     GOE(i,1,b){
    22         sum += cnt[j];
    23         j = nxt[j];
    24     }
    25     cout << sum/d << endl;
    26 
    27     return 0;
    28 }

    暂时只补了这么多题目。太难了暂时还补不了,望谅解。

  • 相关阅读:
    Start The Life of Blog
    当你在寻找某件东西时,眼睛是闭着的
    终于升成512m,以此篇纪念一下
    昨天 acm 分组
    FAQ
    VNC
    Telnet
    FTP
    DNS
    Samba
  • 原文地址:https://www.cnblogs.com/xenny/p/9645643.html
Copyright © 2011-2022 走看看