18.9.20模拟赛T2 城市 枚举

题目大意:给出$N$个数两两的和共$\frac{N \times (N-1)}{2}$个数,请你求出原来的$N$个数
输入:第一行一个数$N$,第二行$\frac{N \times (N-1)}{2}$个数表示两两之和(不保证有序)
输出:第一行为可行解个数$K$,接下来$K$行每行一个方案,每一个方案的数字从小到大输出,中间有一个空格;方案按字典序从大到小输出。
sample input:
4
3 6 5 4 5 7

sample output:
1
1 2 3 4
数据范围:$2 \leq N \leq 300,$所有$N$个数不超过$10^8$


考试的时候只拿了$N \leq 50$的$60$分不甘心$qwq$
考虑将所有和从小到大排序,设为数列$a_i$,原数列设为$k_i$,那么有$k_1+k_2=a_1,k_1+k_3=a_2$,但是不能知道$k_2+k_3$与$k_1+k_4$的大小关系。然后我们能够发现:如果我们知道$k_1$,那么可以知道$k_2$和$k_3$,然后知道$k_2+k_3$,将其从$a_i$中删掉之后就能知道$k_4$,知道$k_4$并删掉$k_2+k_4,k_3+k_4$之后又能知道$k_5$,以此类推就可以得到整个原数列,而如果其中有任何一步出现了没有在$a_i$中出现的和就表示答案非法。
于是有了$60$分想法:枚举$k_1$的取值,并用$map$维护两两之和的最小值查询与删除操作,时间复杂度为$O($值域$\times N^2logN)$
但是值域达到了$10^8绝对会TLE,于是考虑换一种枚举方式
我们从上面可以知道枚举$k_1$得到$k_2,k_3$,进而得到$k_2+k_3$,那么为什么不去枚举$k_2+k_3$得到$k_1,k_2,k_3$呢?于是考虑枚举$k_2+k_3$,又因为$k_2+k_3$最多是$a_{N+1}$,所以枚举复杂度就变成了$O(N^3logN)$,就可以$AC$此题了
如果需要常数优化可以考虑排序+二分查找或者排序+队列+懒惰堆删除,都比$map$快一些

60pts代码:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3
 4 inline int read(){
 5     int a = 0;
 6     char c = getchar();
 7     while(!isdigit(c))
 8         c = getchar();
 9     while(isdigit(c)){
10         a = (a << 3) + (a << 1) + (c ^ ‘0‘);
11         c = getchar();
12     }
13     return a;
14 }
15
16 int output[12];
17 inline void print(int x){
18     int dirN = 0;
19     if(x == 0)
20         putchar(‘0‘);
21     else{
22         while(x){
23             output[dirN++] = x % 10;
24             x /= 10;
25         }
26         while(dirN--)
27             putchar(output[dirN] + 48);
28     }
29     putchar(‘ ‘);
30 }
31
32 map < int , int > m , m1;
33 vector < int > v;
34 int ans , N , now[311];
35
36 inline void check(int dir){
37     m1 = m;
38     now[1] = dir;
39     for(int i = 2 ; i <= N ; i++){
40         now[i] = m1.begin()->first - dir;
41         if(now[i] > 1e8)
42             return;
43         if(!--m1.begin()->second)
44             m1.erase(m1.begin());
45         for(int j = i - 1 ; j >= 2 ; j--)
46             if(!m1.count(now[i] + now[j]))
47                 return;
48             else
49                 if(!--m1.find(now[i] + now[j])->second)
50                     m1.erase(m1.find(now[i] + now[j]));
51     }
52     ans++;
53     for(int i = 1 ; i <= N ; i++)
54         v.push_back(now[i]);
55 }
56
57 int main(){
58     freopen("city.in" , "r" , stdin);
59     freopen("city.out" , "w" , stdout);
60     N = read();
61     if(N == 1){
62         cout << 0;
63         return 0;
64     }
65     for(int i = 1 ; i <= N * (N - 1) >> 1 ; i++){
66         int a = read();
67         m[a]++;
68     }
69     for(int i = m.begin()->first >> 1 ; m.begin()->first - i <= 1e8 && i ; i--)
70         check(i);
71     print(ans);
72     putchar(‘\n‘);
73     for(int i = 0 ; i < ans ; i++){
74         for(int j = i * N ; j < (i + 1) * N ; j++)
75             print(v[j]);
76         putchar(‘\n‘);
77     }
78     return 0;
79 }

100pts代码:

 1 #include<bits/stdc++.h>
 2 #define M (N * (N - 1) >> 1)
 3 using namespace std;
 4
 5 inline int read(){
 6     int a = 0;
 7     char c = getchar();
 8     while(!isdigit(c))
 9         c = getchar();
10     while(isdigit(c)){
11         a = (a << 3) + (a << 1) + (c ^ ‘0‘);
12         c = getchar();
13     }
14     return a;
15 }
16
17 int output[12];
18 inline void print(int x){
19     int dirN = 0;
20     if(x == 0)
21         putchar(‘0‘);
22     else{
23         while(x){
24             output[dirN++] = x % 10;
25             x /= 10;
26         }
27         while(dirN--)
28             putchar(output[dirN] + 48);
29     }
30     putchar(‘ ‘);
31 }
32
33 vector < int > v;
34 int ans , N , now[311] , num[45001];
35 bool vis[45001];
36
37 inline void check(int dir){
38     if(num[1] + num[2] + num[dir] & 1)
39         return;
40     memset(vis , 0 , sizeof(vis));
41     now[1] = (num[1] + num[2] + num[dir] >> 1) - num[dir];
42     now[2] = (num[1] + num[2] + num[dir] >> 1) - num[2];
43     now[3] = (num[1] + num[2] + num[dir] >> 1) - num[1];
44     vis[1] = vis[2] = vis[dir] = 1;
45     int p = 3;
46     for(int i = 4 ; i <= N ; i++){
47         while(vis[p])
48             p++;
49         now[i] = num[p] - now[1];
50         vis[p] = 1;
51         for(int j = i - 1 ; j >= 2 ; j--){
52             int t = lower_bound(num + 1 , num + M + 1 , now[i] + now[j]) - num , q = t;
53             while(q <= M && num[t] == num[q] && vis[q])
54                 q++;
55             if(q == M + 1 || num[t] != num[q])
56                 return;
57             vis[q] = 1;
58         }
59     }
60     ans++;
61     for(int i = 1 ; i <= N ; i++)
62         v.push_back(now[i]);
63 }
64
65 int main(){
66     freopen("city.in" , "r" , stdin);
67     freopen("city.out" , "w" , stdout);
68     N = read();
69     if(N == 1){
70         cout << 0;
71         return 0;
72     }
73     for(int i = 1 ; i <= M ; i++){
74         num[i] = read();
75         if(N == 2){
76             print(num[i] + 1 >> 1);
77             putchar(‘\n‘);
78             for(int j = 1 ; num[i] - j >= j ; j++){
79                 print(j);
80                 print(num[i] - j);
81                 putchar(‘\n‘);
82             }
83         }
84     }
85     sort(num + 1 , num + M + 1);
86     for(int i = N ; i >= 3 ; i--){
87         check(i);
88         while(i > 3 && num[i - 1] == num[i])
89             i--;
90     }
91     print(ans);
92     putchar(‘\n‘);
93     for(int i = 0 ; i < ans ; i++){
94         for(int j = i * N ; j < (i + 1) * N ; j++)
95             print(v[j]);
96         putchar(‘\n‘);
97     }
98     return 0;
99 }

原文地址:https://www.cnblogs.com/Itst/p/9748309.html

时间: 2024-08-22 17:38:45

18.9.20模拟赛T2 城市 枚举的相关文章

2017 9 11 noip模拟赛T2

#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N=205; int map[N][N]; int d[N],tag[N],book[N],f[N]; int n,m; void work(int x) { memset(d,63,sizeof(d)); memset(book,0,sizeof(book)); memset(f,0,sizeof(

【单调栈维护连续区间】2019.1.18模拟赛T2 浇花

这道题是一道单调栈的题 1 题目描述 2 JDFZ在餐厅门前种了一排nn棵花,每棵花都有一个高度.浇花大爷会枚举所有的区间,然后从区间中找出一个高度最矮的花进行浇水.由于浇花大爷浇完水之后就精疲力竭了,所以请你帮助他计算每棵花都被浇了几次水. 3 4 输入格式 5 第一行一个整数nn. 第二行nn个整数,分别表示每棵花的高度. 6 7 输出格式 8 一行nn个整数用空格隔开,分别表示每棵花被浇了几次水. 9 10 样例一 11 input 12 3 13 1 3 5 14 output 15 3

2017/9/3模拟赛T2

题解:由于题目已经提示我们这是个单峰函数,所以很容易想到三分法,所以我们就枚举中位数,为保证平均数最大,左右两侧都从右往左取数. 代码如下: 1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 #define MN 100005 5 using namespace std; 6 int n,a[MN],maxl,l,r,lm,rm; 7 long long sum[MN]; 8 double a

20180530模拟赛T2——绀碧之棺

题目背景Background qiancl 得到了一张藏宝图,上面写了一道谜题. 题目描述 Description 定义\(F(n)\)为 n 在十进制下各个数位的平方和,求区间\([a,b]\)中有多少\(n\)满足\(k\times F(n) = n\). 输入描述 (coffin.in) Input Description 一行三个正整数\(k,a,b\). 输出描述 (coffin.out) Output Description 一行一个整数表示满足条件的\(n\)的个数. 样例输入Sa

20161022 NOIP模拟赛 T2 解题报告

旅行者问题 [问题描述] lahub是一个旅行者的粉丝,他想成为一个真正的旅行者,所以他计划开始一段旅行.lahub想去参观n个目的地(都在一条直道上).lahub在起点开始他的旅行.第i个目的地和起点的距离为ai千米(ai为非负整数).不存在两个目的地和起点的距离相同. 从第i个目的地走到第j个目的地所走的路程为 |ai-aj|千米.我们把参观n个目的地的顺序称作一次“旅行”.lahub可以参观他想要参观的任意顺序,但是每个目的地有且只能被参观一次(参观顺序为n的排列). lahub把所有可能

洛谷 U361 序列操作(NOIP模拟赛T2)

题目链接:https://www.luogu.org/problem/show?pid=U361 题目背景 夏令营 题目描述 小B有一个整数序列a[1..N],初始时序列中所有元素均为0.他会在序列上进行下面两种操作,操作共M个: A l r x:将a[l..r]均加上x. Q l r:询问a[l..r]中的最大值. 输入输出格式 输入格式: 第一行,两个整数N, M. 接下来的M行,每行一个操作. 输出格式: 设询问操作有T个,则输出T行,每行一个整数,表示询问操作对应的答案. 输入输出样例

『8.20 模拟赛』旋转的多边形

题目链接戳着里!! 题目描述 解题思路 显然,多边形滚动的时候,指定的点一定是绕着某一个顶点旋转的,旋转的半径就是点到顶点的距离,角度就是顶点所在脚的外角. 如下图所示: 那么我们的问题就转化成了求dis和θ了. dis很简单,只要勾股定理就好了. 那θ呢?也很简单喽,只要链接当前顶点的相邻的两个顶点,运用余弦定理求就好啦,注意一下角度值和弧度制就可以了. (不会余弦定理的小伙伴戳这里==>  Wikipedia & 百度百科) 代码 1 #include<iostream> 2

『8.20 模拟赛』冒泡排序

题目描述 给定n,k,和一个长度为n的序列,请输出这个序列冒泡排序k次之后的结果. 解题思路 我们观察上面给出的伪代码,可以发现这是一段把代码排序成升序的代码,那我们来考虑一下冒牌排序的几个特征. 一个大的数要向右交换,但是一次交换之后就可以换很多位置,所以换一次就不知道跑到哪里去了,所以很难维护. 一个小的数每次最多和它左边的更大的数交换一次,所以一次最多向左边跑一个位置,比大数不知道好维护到那里去了. 进一步思考,k次交换之后,最多可以向左跑k步,也就是说k+1位置的数最远能跑到1的位置上来

2018.02.12 noip模拟赛T2(未完待续)

二兵的赌注 Description游戏中,二兵要进入了一家奇怪的赌场.赌场中有n个庄家,每个庄家都可以猜大猜小,猜一次一元钱.每一次开彩前,你都可以到任意个庄家那里下赌注.如果开彩结果是大,你就可以得到你之前猜大的庄家相应的ai元钱.如果开彩结果是小,你就可以得到你之前猜小的庄家相应的bi元钱.你可以在同一个庄家那里既猜大又猜小,(这样是两块钱),也可以什么都不猜(这样不用钱).问怎么样下注,才能赢得最多的有保障的钱.有保障的钱指不管开彩结果是大是小,你都能够赢得相应的钱.你能帮助他计算这个值吗