第八届“图灵杯”neuq-acm程序设计竞赛个人赛-题解

@第八届“图灵杯”neuq-acm程序设计竞赛个人赛-题解

A 切蛋糕
题解:
首先可以对 进行二进制拆分,得到其小数点后14位的二进制值。显而易见,若要将蛋糕平均分成k
份,则其二进制下每一个1处,都应有k份与之对应的蛋糕。例如,对于k=5, 的二进制为
0.001100110011,则应存在5份大小为0.001的蛋糕,5份大小为0.0001的蛋糕,以此类推。合并时,
每次将其二进制下1的个数份蛋糕合并,即可得到规定大小的值。所以在拆分时,只需将 在二进制下
每一位为1的大小的蛋糕保存k份,其余蛋糕全部向下拆分,最后再花费k步将蛋糕合并即可。由于误差
不应大于 ,所以最坏情况下只需花费 次即可完成拆分,则最坏情况下总操作次数
为 ,小于6000,满足要求。
其实对于本题,由于放宽了数据要求,存在一个简单的做法,只需暴力花费2047次将所有蛋糕大小都拆
分成 ,再通过计算得到x,使得 。再花k步,每步将x个蛋糕合并,总操作
次数 也可满足要求。
参考代码:

#include <bits/stdc++.h>
using namespace std;
const double eps=1e-10;
bool v[20];
int num[20];
vector<int> op;
int main() {
int k;
cin>>k;
double tmp=1.0/k;
double sum=0.0,cnt=1.0;
for(int i=0; i<15; i++) {
if(sum+cnt-tmp<=eps) {
sum+=cnt;
v[i]=1;
}
cnt/=2;
}
num[0]=1;
for(int i=0; i<15; i++) {
int ret=0;
if(v[i])
ret=k;
while(num[i]>ret) {
op.push_back(i);
num[i]--;
num[i+1]+=2;
}
}
vector<int> ans;
for(int i=0; i<=15; i++) {
if(v[i])
ans.push_back(i);
}
cout<<op.size()+k<<endl;

B 小宝的幸运数组
题解:
用前缀和a[]预处理数组,则从第i位到第j位的子串和为a[j]-a[i-1],又(a-b)%k=a%k-b%k,所以a[j]-a[i-1]
整除k等价于a[j]和a[i-1]对k取模的结果相等,然后只需要贪心的寻找距离最远的相同取模结果。
参考代码:

for(auto t:op){
cout<<1<<" "<<t<<endl;
}
for(int i=1; i<=k; i++) {
cout<<2<<" "<<ans.size();
for(auto t:ans) {
cout<<" "<<t;
}
cout<<endl;
}
return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
#define inf 0x3f3f3f3f
#define intq() int T;cin>>T;while(T--)
ll a[N];
int main () {
intq(){
int t[N]={0},xm[N],ym[N]={0};
for(int i=0;i<N;i++)xm[i]=inf;
int n,p;cin>>n>>p;
bool A=1;
t[0]=1;xm[0]=ym[0]=0;
for(int i=1;i<=n;i++){
ll x;cin>>x;
a[i]=(a[i-1]+x)%p;
xm[a[i]]=min(xm[a[i]],i);
ym[a[i]]=max(ym[a[i]],i);
t[a[i]]++;
if(t[a[i]]>=2)A=0;
}
if(A)cout<<"-1\n";
else{
int ans=0;
for(int i=0;i<N;i++){
if(t[i]>=2){
ans=max(ans,ym[i]-xm[i]);
}
}
cout<<ans<<"\n";
}
}
return 0;

C 上进的凡凡
题解:
一个非降序数组的所有子串都为非降序,只要把给定数组分为若干个非降序部分(每个部分要尽可能
长),即从每个 的地方断开,然后计算子串数,对于长度为n的数组子串数为 ,遍历
然后相加。
参考代码:
D Seek the Joker I
题解:
巴什博弈。
参考代码:

}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
#define fo1(i,n) for(int i=1;i<=n;i++)
int a[N];
ll f(ll x){
ll ans=((x+1)*x)/2;
return ans;
}
int main()
{
int n;cin>>n;
fo1(i,n)cin>>a[i];
ll t=0;
ll ans=0;
fo1(i,n){
if(a[i]>a[i+1]||i==n){
ans+=f(i-t);
t=i;
}
}
cout<<ans;
return 0;
}
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
int n,k;
cin>>n>>k;
if((n-1)%(k+1)!=0) cout<<"yo xi no forever!\n";

E Seek the Joker II
题解:
威佐夫博弈。
参考代码:
F 成绩查询ING
题解:
题目告知查询方式两种,分别以成绩和名字作为查询下标,因此在读入过程中,可以此建立相应的数据
结构,set s[],下标为成绩,存入相应成绩的姓名,mp1,mp2,mp3,是三个map,以姓名为下标,分别
存储成绩,学号和性别。
参考代码:

else cout<<"ma la se mi no.1!\n";
}
return 0;
}
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
double mysticalConstant = (1.0+sqrt(5.0))/2.0;
int T;
cin>>T;
while(T--){
int n,x;
cin>>n>>x;
int a=x-1,b=n-x;
if(a>b) swap(a,b);
int temp = (b-a)*mysticalConstant;
if(temp!=a) cout<<"yo xi no forever!\n";
else cout<<"ma la se mi no.1!\n";
}
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
set<string> s[120];
map<string,int> mp1;
map<string,int> mp2;
map<string,int> mp3;
for(int i=1;i<=n;i++)
{
string Name;
int xb;

G 贪吃的派蒙
题解:
先找出派蒙的位置,计算出它左边的人一轮下来所要吃的最少数量Amin和最多数量Amax,另外还要计
算出一轮下来除派蒙外其余人所要吃的最少数量和最多数量。
如果Amin<=k<=Amax,那么左边的人肯定能在轮到派蒙吃时正好吃完蛋糕。
如果不能的话,那么甜甜花酿鸡就要被派蒙吃掉Ax个,接下来我们加上一轮下来其余人所要吃的最少数
量和最多数量,重复上述步骤判断。
参考代码:

int xh,cj;
cin>>Name>>cj>>xb>>xh;
s[cj].insert(Name);
mp1[Name]=cj;
mp2[Name]=xh;
mp3[Name]=xb;
}
int m;
cin>>m;
while(m--){
int x;
cin>>x;
if(x==1){
string name;
cin>>name;
cout<<mp1[name]<<" "<<mp2[name]<<" "<<mp3[name]<<endl;
}else{
int y;
cin>>y;
set<string>::iterator it;
it=s[y].begin();
for(;it!=s[y].end();it++){
cout<<*it<<"\n";
}
}
}
return 0;
}
#include<bits/stdc++.h>
#define sc scanf
#define pr printf
#define endl '\n'
#define ll long long
#define lnode node*2,start,mid
#define rnode node*2+1,mid+1,end
#define rep(i,a,b) for(ll i=a;i<=(b);i+=1)
#define input freopen("input.txt", "r", stdin)
#define output freopen("output.txt", "w", stdout)
#define To_string(num,str) {stringstream ss;ss<<num;ss>>str;}
#define To_num(str,num) {stringstream ss;ss<<str;ss>>num;}
const int maxn=(1e5+10);
using namespace std;
struct node
{
int id;
int val;
}b[maxn];
ll a[maxn];
bool cmp(node a,node b)
{
return a.val>b.val;
}
int main()
{
cin.tie(0);cout.tie(0);
ios::sync_with_stdio(false);
//input;
//output;
int t;
cin>>t;
while(t--)
{
ll n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i].id=i;
b[i].val=a[i];
}
sort(b+1,b+1+n,cmp);
int gs=b[1].id;
ll amin=0,amax=0,bmin=0,bmax=0;
for(int i=1;i<gs;i++)
{
amin+=1;
amax+=a[i];
}
for(int i=1;i<=n;i++)
if(i!=gs)
{
bmin+=1;
bmax+=a[i];
}
ll l=(k-amax)/(bmax+a[gs]),r=(k-amin)/(bmin+a[gs]);
if((k-amax)%(bmax+a[gs])!=0) l+=1;
if(k>=amin&&k<=amax)
{
cout<<"YES"<<endl;
continue;
}
if(l<=r&&l>=1)
{

H 数羊
题解:
暴力打表可知:
m=0时,A(n,0)=n+2;
m=1时,A(n,1)=2*n
m=2时,A(n,2)=2^n
注意取模。
证明如下:
参考代码:

cout<<"YES"<<endl;
continue;
}
cout<<"NO"<<endl;
}
return 0;
}
/*
4 3
1 2 3 2
5 8
1 2 3 2 1
*/
#include<bits/stdc++.h>
#define ll long long
#define lnode node*2,start,mid
#define rnode node*2+1,mid+1,end
#define rep(i,a,b) for(ll i=a;i<=(b);i+=1)
#define input freopen("in.txt", "r", stdin)
#define out freopen("out.txt", "w", stdout)

I 买花
题解:
假设第一天买花x朵,买k天,则一共买花x+2x+…+2(k-1)x朵,即 朵,又2≤k≤15,直接枚举14种
情况n是否能整除 。
参考代码:

#define To_string(num,str) {stringstream ss;ss<<num;ss>>str;}
#define To_num(str,num) {stringstream ss;ss<<str;ss>>num;}
const double pi=acos(-1.0);
const int maxn=(5e5+10);
const int inf=0x3f3f3f3f;
const ll mod=998244353;
using namespace std;
ll quick_mod(ll a,ll m)
{
ll ans=1;
while(m!=0)
{
if(m%2==1) ans=ans*a%mod;
a=a*a%mod;
m/=2;
}
return ans%mod;
}
int main()
{ cin.tie(0);
ios::sync_with_stdio(false);
int t;
ll n,m;
//input;
//out;
cin>>t;
while(t--)
{
cin>>n>>m;
ll ans;
if(m==0)
{
ans=n+2;
}
else if(m==1)
{
ans=n*2;
}
else if(m==2)
{
ans=quick_mod(2,n);
}
cout<<ans%mod<<endl;
}
return 0;
}

J 这是一道简单的模拟
题解:
本题中n较小,且为无向图,直接用二维数组建图即可。遍历推荐路径,寻找最优方案。注意初始化。
参考代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int T;
cin>>T;
while(T--){
bool A=1;
int n;cin>>n;
for(int i=2;i<=16;i++) {
ll x=pow(2,i)-1;
if(n%x==0){
cout<<"YE5\n";
A=0;
break;
}
}
if(A)cout<<"N0\n";
}
}
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 10005
#define inf 0x3f3f3f3f
#define mem(A) memset(A,0,sizeof(A))
int a[N][N];
int t[N];
bool judge(int n){
for(int i=1;i<=n;i++){
if(t[i]!=1)return 0;
}
return 1;
}
int main(){
int n,m;cin>>n>>m;
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)a[i][j]=inf;
while(m--){
int x,y,z;
cin>>x>>y>>z;
a[x][y]=a[y][x]=z;
}
int k;cin>>k;
int ans=inf;
while(k--){
int nn;cin>>nn;
mem(t);

K 黑洞密码
题解:
本题为简单的模拟题目,题目告知每四个字母和数字为一组,利用队列先进先出的特性,用循环遍历字
符串,将字母和数字分别存入到两个队列中,进行转换操作。输出答案。
参考代码:

bool A=1;
int x=0,y=0,s=0;
for(int i=1;i<=nn;i++){
cin>>y;t[y]++;
if(a[x][y]!=inf){
s+=a[x][y];
}
else A=0;
x=y;
}
if(a[y][0]!=inf){
s+=a[y][0];
}
else A=0;
if(A&&judge(n))ans=min(ans,s);
}
if(ans==inf)cout<<"-1";
else cout<<ans;
return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main(){
string str;
cin>>str;
int len=31;
queue<char> q;
queue<int> s;
for(int i=0;i<=31;i++){
if((str[i]<='z'&str[i]>='a')||(str[i]<='Z'&str[i]>='A')){
q.push(str[i]);
}else
{
s.push(str[i]-48);
}
}
string s1;
while(!q.empty()){
char ss[5];
for(int i=1;i<=4;i++){
ss[i]=q.front();
q.pop();
}
int sss[5];
for(int i=1;i<=4;i++){
sss[i]=s.front();
s.pop();

L 建立火车站
题解:
题目需要在一条直线上不同城市(城市算已有站点)之间建立站点,问如何建立站点能使本条路线上最
大值达到最小值,若采用暴力的方法,假设最大值为1,2,3以此类推,时间复杂度过大,因此不行,
排除,采用对最大值二分的思路,搜索最大值的最小值。
参考代码:

}
for(int i=1;i<=4;i++){
if(ss[i]>'Z'){
if(ss[i]+sss[i]>'z'){
ss[i]=ss[i]+sss[i]-'z'+'A';
}else
ss[i]+=sss[i];
}else{
if(ss[i]+sss[i]>'Z'){
ss[i]=ss[i]+sss[i]-'Z'+'a';
}else
ss[i]+=sss[i];
}
}
for(int i=4;i>=1;i--){
s1+=ss[i];
}
}
cout<<s1<<endl;
}
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long a[MAXN];
int ll,n,k;
long long judge(long long mid)
{
int m=0;
for(int i=2;i<=n;i++)
if(a[i]-a[i-1]>=mid)
{
m+=(a[i]-a[i-1])/mid;
if((a[i]-a[i-1])%mid==0)
m--;
}
if(m>k)
return 0;
return 1;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
long long l=0;
long long r=1000000000000;
while(l<r)
{
long long mid=(l+r)/2;
if(judge(mid)==1)
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值