第八届“图灵杯”NEUQ—ACM程序设计竞赛个人赛(同步赛)

A题——切蛋糕

龙龙有一块蛋糕,现在他想将蛋糕平均切成k块,分给他的k名hxd。但是不幸的是,因为龙龙不擅长切蛋糕,他每一次只能将一块蛋糕平均分成两份。例如,将一块大小为1的蛋糕分割成两块大小为1/2的蛋糕,将一块大小为1/2的蛋糕分割成两块大小为1/4的蛋糕,以此类推。由于龙龙手法有限,蛋糕的大小最小不能小于 1/215 .

除此之外,为了更有仪式感,龙龙在将切好的蛋糕分给自己的hxd之前,要先将蛋糕打包。龙龙可以将切好的任意数量块蛋糕打包在一起,并交给他的一位hxd。需要注意的是,蛋糕在打包好后就不能再被切分了。为了公平起见,龙龙希望他的每一位hxd分得的蛋糕大小是相等的,即每位hxd分得的蛋糕的大小,与1/k的差距的绝对值不能大于1/210

因为龙龙很懒,所以他希望蛋糕的总操作次数(每次打包或切分算一次操作)不超过6000。

现在龙龙想请你为他设计一种方案,使得他能够在6000次操作内,将蛋糕公平地分给他的hxd们。

输入样例:
在这里给出一组输入。例如:

2
输出样例:
在这里给出相应的输出。例如:

3
1 0
2 1 1
2 1 1

分析:如果我们把1划分成x份,那么每份就是1/x。
我们希望最后的k个人得到的尽可能平均,那么每个人必然是拿到[x/k]份的1/x或者[x/k]+1份的1/x。
那么每个人得到的值,与平均值x/k的差值是不可能大于1/x的。

题目要求差值不超过1/210,那么我们把1切成x=1024份,每份的值为1/1024,再平均分配打包给所有人就是了。

切割成1024份需要的次数为21+22+…+29=1023次,在加上打包k次,次数必定在6000次内。
(当然你切成2048份也行,仍然在6000次内)

//隔壁学长分析的好^_^ ....与我无瓜 先占坑咳咳
#include<bits/stdc++.h>
#define ll long long
#define INF 0x7f7f7f7f //2139062143
#define llINF 9223372036854775807
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
using namespace std;
const int maxn=30+7;
const int mod=998244353;

int k;
int ans=1023;

int main()
{
   
    scanf("%d",&k);
    printf("%d\n",ans+k);
    int now=1;
    for(int i=0;i<=9;i++)
    {
   
        for(int j=0;j<now;j++)
            printf("1 %d\n",i);
        now*=2;
    }
    int num=1024;
    int a=num/k,b=num%k;
    for(int i=1;i<=k;i++)
    {
   
        printf("2");
        if(i<=b)
        {
   
            printf(" %d",a+1);
            for(int i=0;i<=a;i++) printf(" 10");
            printf("\n");
        }
        else
        {
   
            printf(" %d",a);
            for(int i=0;i<a;i++) printf(" 10");
            printf("\n");
        }
    }
}

B题——小宝的幸运数组

对于小宝来说,如果一个数组的总和能够整除他的幸运数字k,就是他的幸运数组,而其他数组小宝都很讨厌。现在有一个长度为n的数组,小宝想知道这个数组的子数组中,最长的幸运子数组有多长。

对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组b获得数组a,则称数组a是数组b的子数组。(子数组包含原数组,但不包含空串)

输入格式:
多组输入。第一行包含一个整数T(1≤T≤10),表示有T组测试数据。

每组测试数据包含两行,第一行包含两个整数n和k(1≤n≤105)
分别表示数组长度和小宝的幸运数字。第二行包含n个空格分隔的整数a(0<ai<109),为数组的元素。

输出格式:
对于每组数据,输出和能被k整除的最长子数组的长度。如果没有这样的子数组,则输出−1。每组数据输出一行。

输入样例:
在这里给出一组输入。例如:

4
3 3
1 2 3
3 5
1 2 3
3 7
1 2 3
1 6
5
输出样例:
在这里给出相应的输出。例如:

3
2
-1
-1

当时比赛我把子数组的定义看成单调了我淦。。

分析:首先在给定复杂度下,最朴素的做法就是双指针遍历,每次遍历时求和取余判断,时间复杂度为O(n3)炸掉
再此基础上,如果我们用前缀和代替每次就和,时间复杂度就会降到O(n2),1e5的数据还是没法过,
回归取余定义:(a-b)%k =a%k- b%k,能整除就意味着二者相等,那也就是,对于前缀和a,b他们的位置模K是相同的,也就是,我们先记下这个值第一次出现的位置,然后贪心,求出最大长度,时间复杂度就降到O(n)了

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

map<int,int> mp;
vector<long long> ans;
typedef long long ll;
const int N = 1e5+10;
ll a[N];

int main()
{
   
	 
	 int t = 0;
	 scanf("%d",&t);
	 
	 while (t --)
	 {
   
	 	 ll n , k=0;
	 	 ll sum[N];
         ll vis[N];
	 	 memset(sum,0,sizeof(sum));
	 	 memset(a,0,sizeof(a)); 
         memset(vis,-1,sizeof(vis)); //先标定假设这些子序列都不存在

	 	 
	 	 scanf("%d%d",&n, &k);
	 	 
	 	 for (int i = 1 ;i <= n ; i++) scanf("%d",&a[i]);
	 	 
	 	 for (int i =1 ;i<=n ;i++)
         {
   
             sum[i] = sum[i - 1] + a[i];//预处理
             sum[i] %= k; //先取模
         }
	 	 
         ll ok = -1;
	 	 for (ll i = 0 ; i <= n ;i++)
         {
   
             if (vis[sum[i]] == -1) vis[sum[i]] = i; //先挖坑,给每个值存放位置
             else ok = max (ok , i - vis[sum[i]]); //如果下次遇到就直接贪心
         }
             
 		  printf("%d\n",ok);如果没有遇到相同的就只是挖坑没有贪心,直接退出ok = -1}
	 
	 return 0;
}

C——上进的凡凡

凡凡是一个上进的人,他的人生没有下坡路,他也讨厌带有”下坡路“的东西。

所以,对于凡凡来说,只有非降序的数组才是nice的(如:1,2ÿ

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值