洛谷补题传送门
思路:要满足乘积不为0,只需要所有的数均不为0即可;要满足和不为0,在任意一个数加一即可。因此只需要记录0的数量cnt
和所有数的和sum
,让所有的0加一之后,看和是否为0,如果和为0,再找一个不是-1的数加一即可。
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 200005
int t, sum, cnt, n;// cnt 记录0的个数,sum记录所有数的和
int main()
{
cin >> t;
while (t--)
{
cin >> n;
cnt = 0; sum = 0;//初始化
while (n--)
{
int tmp;
cin >> tmp;
if (tmp == 0) cnt++;
sum += tmp;
}
cout << cnt + (sum + cnt == 0) << endl; // 对所有0加一之后如果和为0还要再进行一次操作
}
return 0;
}
洛谷补题传送门
思路:可以验证,将数列排序后对半分,两个中位数必不可能落在同一边。所以,取最中间的两个数作为两个中位数是最优的选择。
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 200005
int n, t;
int a[maxn];
int main()
{
cin >> t;
while (t--)
{
cin >> n;
for (int i = 0; i < (n << 1); i++)
{
cin >> a[i];
}
sort(a, a + (n << 1));
cout << a[n] - a[n - 1] << endl;
}
return 0;
}
洛谷补题传送门
思路:先分析 f ( x , y ) = ( x ∣ y ) − y f(x,y)=(x|y)-y f(x,y)=(x∣y)−y,对于|运算,其与+运算不同的地方在于 1 ∣ 1 = 1 1|1=1 1∣1=1,而 1 + 1 = ( 10 ) 2 1+1=(10)_2 1+1=(10)2,所以 f ( x , y ) = ( x + y ) − y − ( ( x + y ) − ( x ∣ y ) ) = x − ( 二 进 制 下 x 与 y 同 是 1 的 部 分 组 成 的 数 ) f(x,y)=(x+y)-y-((x+y)-(x|y))=x-(二进制下x与y同是1的部分组成的数) f(x,y)=(x+y)−y−((x+y)−(x∣y))=x−(二进制下x与y同是1的部分组成的数),例如 f ( 27 , 14 ) = 27 − ( ( 11011 ) 2 和 ( 1110 ) 2 同 是 1 的 部 分 组 成 的 数 ) = 27 − ( 1010 ) 2 = 17 f(27,14)=27-((11011)_2和(1110)_2同是1的部分组成的数)=27-(1010)_2=17 f(27,14)=27−((11011)2和(1110)2同是1的部分组成的数)=27−(1010)2=17。因此原式就相当于 a 1 − ( 二 进 制 下 a 1 与 a 2 ∣ a 3 ∣ . . . ∣ a n 同 是 1 的 部 分 组 成 的 数 ) a_1-(二进制下a_1与a_2|a_3|...|a_n同是1的部分组成的数) a1−(二进制下a1与a2∣a3∣...∣an同是1的部分组成的数),因此我们只要把 a 1 a_1 a1选出来就行了。
对所有的位上面的1的数量进行统计,如果多于一个数或者没有一个数在某一位上面是1的话,那么无论 a 1 a_1 a1是哪个数,结果的这一位上一定是0;如果只有一个数在某一位上面是1的话,只要让这个数作为 a 1 a_1 a1,那么最终结果这一位上就是1了。因此,要取满足这样的条件的 t t t 和 n n n :只有一个数在第 t t t 位是1,而这个数的下标是 n n n。要使结果最大,则 t t t 要最大,此时将原数列的 a n a_n an作为新数列的 a 1 a_1 a1即可。
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 200005
int n, t, id = -1;// 记录要放在第一的数的下标
int b[maxn];
int a[100][2];// a[i][0]记录有多少个数在第i位是1,a[i][1]记录最近的在第i位是1的数的下标
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
int tmp, t = 0;//用tmp来遍历所有的位,t表示当前遍历到的位数
cin >> tmp;
b[i] = tmp;
while (tmp)
{
if (tmp & 1)//如果该位是1
{
a[t][0]++;
a[t][1] = i;
}
tmp >>= 1;
t++;
}
}
for (int i = 40; i >= 0; i--)//从高到低遍历,遇到第一个满足条件的位数t即停止
{
if (a[i][0] == 1)
{
id = a[i][1];
break;
}
}
if (id == -1)//如果没有满足条件的位数t,则说明随便排结果都一样是0
{
for (int i = 0; i < n; i++)
{
cout << b[i] << ' ';
}
return 0;
}
cout << b[id];//将第id个数先打印放在第一
for (int i = 0; i < n; i++)
{
if (i != id)//跳过第id个数依次打印
cout << ' ' << b[i];
}
return 0;
}
洛谷补题传送门
思路:先随便尝试了一下,发现平行四边形,拉长的正六边形什么的都符合条件,而三角形,五边形什么的都不行,于是猜测只要对边平行且相等的2k边形均符合条件。写完就过了。没有严谨证明。(不知道是不是这样)
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 100005
#define ll long long
int n;
ll a[maxn][2];
int judge_para(ll x1, ll y1, ll x2, ll y2) // 判断两个向量是否刚好互为相反向量
{
return x1 == -x2 && y1 == -y2;
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
scanf("%lld%lld", &a[i][0], &a[i][1]);
}
if (n & 1) printf("NO");// n为奇数连对边都找不到
else // 只要满足对边平行且相等(对边对应的向量相等或者相反),即能输出YES
{
if (!judge_para(a[0][0] - a[n - 1][0], a[0][1] - a[n - 1][1],
a[n >> 1][0] - a[(n >> 1) - 1][0], a[n >> 1][1] - a[(n >> 1) - 1][1]))
{
printf("NO");
}
else
{
for (int i = 1; i < (n >> 1); i++)
{
if (!judge_para(a[i][0] - a[i - 1][0], a[i][1] - a[i - 1][1],
a[(n >> 1) + i][0] - a[(n >> 1) + i - 1][0], a[(n >> 1) + i][1] - a[(n >> 1) + i - 1][1]))
{
printf("NO");
return 0;
}
}
printf("YES");
}
}
return 0;
}
洛谷补题传送门
思路:最优的结果一定是一个非递减的序列,否则可以将连续递减的子序列进行“平均”处理即可得到更优的解。因此很简单的想法就是维护序列的非递减性。
AC代码:
#include
#include
#include
#include
#include
#include
using namespace std;
#define maxn 1000005
#define ll long long
int n, top = -1;// top是当前最大的下标,类比栈
ll a[maxn][2];// a[i][0]代表第i个块的总和,a[i][1]代表第i个块包含的数的个数
// 记录和(整数)比记录平均值(浮点数)更准确
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
top++;
scanf("%lld", &a[top][0]);//先写入数组的结尾
a[top][1] = 1;// 新的块包含1个数
// 检查是否需要合并,如果新合并的块比前一个块的平均值小则进行合并
while (top > 0 && a[top][0] * a[top - 1][1] < a[top - 1][0] * a[top][1])// 用乘法代替除法进行比较,效率和准确性都更高
{
top--;
a[top][0] += a[top + 1][0];
a[top][1] += a[top + 1][1];
}
}
// 每一块内部都进行平均处理
for (int i = 0; i <= top; i++)
{
double tmp = (double)a[i][0] / (double)a[i][1];
for (int j = 0; j < a[i][1]; j++)
{
printf("%.10lf\n", tmp); // 输出10位小数,保证误差不超过1e-10
}
}
return 0;
}
这次感觉不算太难,但是被第四题卡了太久,还是要继续努力啊!