Codeforces Round #677 (Div. 3) 题解(A~F)

A题:无聊之公寓(Boring Apartments)
极其水的签到题:给出T组数据,再给定一个整数x,保证x是a,aa,aaa或aaaa的形式,然后求出在这个序列中:
1,11,111,1111,2,22,222,2222,3······9999
x之间的数字一共有几位,例如x=22,则
ans=1+2+3+4+1+2
1是一位,11是两位,111是三位,1111是四位,2是一位,22是两位
所以答案就是13。
代码·······随便吧:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt T,n,ans; 
int main()
{
 T=read();
 while(T--)
 {
  n=read();
  rt t=n%10;
  ans=0;
  for(int i=1;i<t;i++)ans+=10;
  if(n>1000)ans+=10;
  else if(n>100)ans+=6;
  else if(n>10)ans+=3;
  else ans++;
  printf("%lld\n",ans); 
 }
 return 0;
 } 

B题:然而还有另一个书架(Yet Another Bookshelf)
T组数据,给定一个长度为n的二进制序列,每一次我们可以移动一段连续的1,每一次可以左移1或者右移1,问最少需要多少步才能将所有的1都移动到一起
解法:鉴于移动的本质相当于是消除两端1之间的0,所以只需把两端的0舍弃,然后统计剩余的0的个数即为最小
wck:这傻逼题只有傻逼才能做错·······等会我怎么WA了?
代码:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt T,n;
rt a[100],ans,l,r;
rt ansl,ansr,anss;
int main()
{
 T=read();
 while(T--)
 {
  n=read();
  ans=0;
  for(int i=1;i<=n;i++)
   a[i]=read();
   l=1,r=n;
  while(a[l]==0)l++;
  while(a[r]==0)r--;
  for(int i=l;i<=r;i++)
  if(a[i]==0)ans++;
  printf("%lld\n",ans);
 }
 return 0;
}

C题:大鱼吃小鱼(Dominant Piranha)
T组数据,n个整数,每相邻两个整数较大的那个可以吞食较小的那个,较大的整数+1,问最后剩下哪一个,输出其原始下标
这题更尼玛离谱:只要找到所有数中最大的那一个,并且最大的那个至少可以吞食左边或者右边其中一个,那么答案就是这个整数了
代码:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt T,n,ans,a[300010],maxn;
int main()
{
 T=read();
 while(T--)
 {
  n=read();
  ans=-1;maxn=0;
  for(int i=1;i<=n;i++)
   a[i]=read(),maxn=max(maxn,a[i]);
  a[n+1]=a[0]=0x3f3f3f3f3f3f3f3f;
  for(int i=1;i<=n;i++)
  if(maxn==a[i]&&(a[i-1]<a[i]||a[i+1]<a[i]))
  {
   ans=i;
   break;
  }
  printf("%lld\n",ans);
 }
}

D题:连接地区(Districts Connection)
给出T组数据,n个结点,我们现在要建造n-1条双向道路将n个节点连接,但是每个结点可能属于某一个组,每条双向道路两端节点不能在同一个组内部。如果能连接成功,则输出“YES”和方案,若不能则输出"NO"。
解法:鉴于n的量级很小,可以直接采取暴力策略:
n^2复杂度枚举任意两点,若可连接则将其连接,若不能则放弃寻找下一组
判定可连接必须是既不在同一个组里,也没有被并查集并在用一个集合里。
wck:不会吧不会吧,不会真有人这题都写错······艹写成Yes和No了
代码:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt T,n,counter,a[5010],f[5010];
rt x[5010],y[5010];
rt find_fa(rt x){return x==f[x]?x:f[x]=find_fa(f[x]);}
int main()
{
 T=read();
 while(T--)
 {
  n=read();
  counter=0;
  for(int i=1;i<=n;i++)f[i]=i;
  for(int i=1;i<=n;i++)
   a[i]=read();
  for(int i=1;i<=n;i++)
  {
   for(int j=i+1;j<=n;j++)
   if(a[i]!=a[j])
   {
    rt fx=find_fa(i);
    rt fy=find_fa(j);
    if(fx!=fy)
    {
     if(rand()%2)
     f[fx]=fy;
     else f[fy]=fx;
     counter++;
     x[counter]=i;y[counter]=j;
     if(counter==n-1)break;
    }
   }
   if(counter==n-1)break;
  }
  
  if(counter==n-1)
  {
   printf("YES\n");
   for(int i=1;i<=counter;i++)
    printf("%lld %lld\n",x[i],y[i]);
  }
  else
   printf("NO\n");
 }
 return 0;
}

E题:两个圆圈舞(Two Round Dances)
n个人标号为1~n,n为偶数,这n个人划分为两个圈,两个圈内的人任意排布,问一共有多少种方案
解法:答案是C[n][n/2]/2*(n/2-1)!*(n/2-1)!,简单来说就是从n个数中选出n/2个一共有多少方案,然后每一种方案里单个圈内元素所有排列的情况总数为(n/2-1)!,两个圈就求一个平方即可,记得开long long
代码:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt n,C[25][25];
int main()
{
 n=read();
 rt x=n/2;
 for(int i=1;i<=n;i++)
 for(int j=0;j<=i;j++)
 if(j==0||j==i)C[i][j]=1;
 else C[i][j]=C[i-1][j-1]+C[i-1][j];
 rt ans=C[n][x]/2;
 rt t=1;
 for(int i=1;i<x;i++)
 t*=i;
 ans=ans*t*t;
 printf("%lld",ans);
 return 0;
}

F题:零记之和(Zero Remainder Sum )
题意:给出一个n*m的矩阵a,设x为floor(m/2),我们要求的是对于每一行选取不超过x个元素,当这些元素之和能整除给定整数k时,和最大是多少
解题:由于n,m,k给出的都在70以下,可以用一个比较暴力一点的动态规划来做:
首先对于每一行:定义dp[i][j][t][s],其含义是第i行 前j个元素选择了t个,其和对k取模后值为s的最大和
对于dp[i][j][t][s],可以转移到dp[i][j+1][t][s]和dp[i][j+1][t+1][(s+a[i][j+1])%k]
然后定义数组af[i][s]表示第i行选择少于x个元素之和对k取模所得结果为s的最大和
af[i][s]=max{dp[i][m][j][s]}(0<=j<=x)
定义数组f[i][s],表示前i行之和对k取模为s的最大和,转移方程:
f[i+1][(s+t)%k]=max(f[i+1][(s+t)%k],f[i][t]+af[i+1][s])
最后输出f[n][0]即可
代码:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define rt long long
using namespace std;
inline rt read()
{
 register char ch=getchar();register rt u=0,v=0;
 while(!isdigit(ch)){if(ch=='-')v=1;ch=getchar();}
 while(isdigit(ch)){u=u*10+ch-'0';ch=getchar();}
 return v?-u:u;
}
rt n,m,k;
rt a[75][75],f[75][75],af[75][75];
rt dp[75][75][75][75];
int main()
{
 n=read(),m=read(),k=read();
 for(int i=1;i<=n;i++)
  for(int j=1;j<=m;j++)
   a[i][j]=read();
 rt x=m/2;
 memset(dp,0xcf,sizeof(dp));
 for(int i=1;i<=n;i++)
 dp[i][0][0][0]=0;
 for(int i=1;i<=n;i++)
  for(int j=0;j<m;j++)
   for(int t=0;t<=min(x,1LL*j);t++)
    for(int s=0;s<k;s++)
    {
     dp[i][j+1][t][s]=max(dp[i][j][t][s],dp[i][j+1][t][s]);
     dp[i][j+1][t+1][(s+a[i][j+1])%k]=max(dp[i][j][t][s]+a[i][j+1],dp[i][j+1][t+1][(s+a[i][j+1])%k]);
    }
 memset(af,0xcf,sizeof(af));
 for(int i=1;i<=n;i++)
 for(int t=0;t<=x;t++)
  for(int s=0;s<k;s++)af[i][s]=max(af[i][s],dp[i][m][t][s]);
 memset(f,0xcf,sizeof(f));
 for(int s=0;s<k;s++)f[1][s]=af[1][s];
 for(int i=1;i<n;i++)
 for(int s=0;s<k;s++)
 for(int t=0;t<k;t++)
  f[i+1][(s+t)%k]=max(f[i+1][(s+t)%k],f[i][t]+af[i+1][s]);
 printf("%lld",f[n][0]);
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值