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;
}