A题链接
签到题,就是给一串数字,找到他的一个合为偶数的子集。
先找有没有偶数,如果有偶数就输出1和偶数的位置;如果没有偶数,找到两个奇数,输出2和两个奇数的位置即可。
#include
#define ll long long
using namespace std;
const int MAXN = 200;
int A[MAXN];
int n;
int main()
{
int T;
scanf("%d", &T);
while (T--)
{
scanf("%d", &n);
int even = 0;
int odd1 = 0;
int odd2 = 0;
for (int i = 1; i <= n; i++)
{
scanf("%d", &A[i]);
}
for (int i = 1; i <= n; i++)
{
if (A[i]%2)
{
if (odd1 == 0)
{
odd1 = i;
}
else
{
odd2 = i;
}
}
else
{
even = i;
}
}
if (even)
{
printf("1\n%d\n", even);
}
else if (odd1 && odd2)
{
printf("2\n%d %d\n", odd1, odd2);
}
else
printf("-1\n");
}
return 0;
}
B题链接
给定三个数,n,m,k;
以及两个01数组,分别是长度为n的a[]和长度为m的b[]。
将a向量与b向量相乘,获得一个矩阵。
问矩阵中,单元数(元素个数)为k的的子矩阵有多少个。
这个挺难搞,真还挺难搞(我的水平相当一般)。
首先我们这样考虑。
将a数组作为向量的列,b数组乘上去就是这一列存不存在,1的时候下一列就还是a向量,0的时候下一列就是全0。
那么我们现实单考虑一列的时候。
假设,k为2,这向量a的这一列是
01110001111’(横的写不占地方)
那么对于这一列,在第二个位置,有一个,第三个位置有一个。
三个1连起来可以有2个,四个一连起来可以有3个。
那么对于每一次这样的访问,我要怎么以O(1)的复杂度,获得这一列到底有多少个长度为k的子集呢?(对于宽度为1的竖棍棍情况)
我们扫一遍A数组,对于连续的1进行统计,统计有多少个连续的1。当遇到0的时候,我们就把这个连续的个数对应的hash值+1,hash数组第一个目标是记录的是长度为X的连续为1的数组有多少个。
(回头想我搞的麻烦了,如果直接连续的算,就不用两次后缀和了,应该,先这样搞吧)。
然后我们需要做两次后缀和(对这个hash的数组),第一次对应的是,如果后面很多个连续的存在(比如说4),那么前面的也存在(比如说1,2,3)。(这时候hash数组记录长度为X的连续为1的数组有多少个)
然后第二次后缀和,是为了O(1)的获取,某个长度对应的情况数量。
(hash数组此时记录长度大于等于X的连续1有多少个)
总之算出来的结果是,对于
111 这个数组
hash数组应该是 3 2 1
1111 就是
4 3 2 1
接着我们考虑,横向的对于B向量又该怎么搞。
对于一个k值,比如说6,我们有可能有很多种行的排列方式,比如说1行,2行,3行和6行。
那么我们如何判断行的数量。
就是扫一遍b数组,记录当前扫过的连续的1的数量为now,如果k能够整除now,那么对应的行数就是k/now。
然后的问题是如何统计数量。
假设我们现在b中连续1的长度比较长,此时k为6,横向一行的长度已经能够填充排成1行,2行,3行和6行的全部情况,那么累加一列上去,对应的以k/now(now=1,2,3,6)为下标的hash数组的值,都需要累加到答案上。(都可以向右再移动一列)
那么我们可以额外用一个变量times,记录每一段b中连续的1,他累加了多少情况在上面(对应排成1行,2行,3行等的总数)。遇到b中为0的元素就清零,然后每次先判断能不能整除now,先加times,再把times累加到答案上。就搞完了。
注意一下k/now不要访问越界了。
#include
#define ll long long
using namespace std;
const int MAXN = 500000;
ll k;
int n, m;
int A[MAXN], B[MAXN];
ll mp[MAXN];
int main()
{
scanf("%d%d%lld", &n, &m, &k);
for (int i = 1; i <= n; i++)
{
scanf("%d", &A[i]);
}
for (int i = 1; i <= m; i++)
{
scanf("%d", &B[i]);
}
int nums = 0;
for (int i = 1; i <= n+1; i++)
{
if (A[i])
{
nums++;
}
else
{
mp[nums]++;
nums = 0;
}
}
for (int i = MAXN-2; i >= 0; i--)
{
mp[i] = mp[i+1]+mp[i];
}
for (int i = MAXN-2; i >= 0; i--)
{
mp[i] = mp[i+1]+mp[i];
}
ll ans = 0;
int now = 0;
ll times = 0;
for (int i = 1; i <= m; i++)
{
if (B[i])
{
now++;
if (k%now == 0 && k/now < MAXN)
{
times += mp[k/now];
}
ans += times;
}
else
{
now = 0;
times = 0;
}
}
printf("%lld", ans);
return 0;
}
C题链接
给一串括号序列,每次移动任意调整子串中括号的顺序最少,每次移动花费的代价是子串的长度,问最少代价是多少。
这个难度我觉得比B题低。
肯定是贪心的先满足顺序不对的括号,所以说只要做两个栈,st1和st2。
从左往右扫。
st1里面存储的是’)'的下标(没有与之对应的右括号)。
st2里面存储的是‘(’,就是可以抵消‘)’的(现在想记个数字就行)。
遇到了‘(’:
当st2里面没东西的时候,就压入到st1中。
如果st2里面有东西,就pop出来,消掉一个。
遇到了‘)’:
如果st1里面有东西,就消掉一个。
如果st1在这次操作中,消空了,那么就说明中间的这一段,都得重新排序,做一次操作,代价是i(循环变量)-最后pop出来的值+1。累积到答案中。
如果st1消除之前就是空的,那么就加到st2中去。
然后就没了。
#include
#define ll long long
using namespace std;
const int MAXN = 2000000;
char s[MAXN];
int n;
int toto, totc;
stack <int> st1;
stack <int> st2;
int main()
{
scanf("%d", &n);
scanf("%s", s+1);
for (int i = 1; i <= n; i++)
{
if (s[i] == '(')
{
toto++;
}
else if (s[i] == ')')
{
totc++;
}
}
if (toto != totc)
{
printf("-1");
}
else
{
ll ans = 0;
for (int i = 1; i <= n; i++)
{
if (st2.empty() && s[i] == ')')
{
st1.push(i);
}
else if (st2.size() && s[i] == ')')
{
st2.pop();
}
else if (st1.size() && s[i] == '(')
{
int tmp = st1.top();
st1.pop();
if (st1.empty())
ans += i-tmp+1;
}
else if (st1.empty() && s[i] == '(')
{
st2.push(i);
}
}
printf("%lld", ans);
}
return 0;
}
emmmmm,就做出来这么三个,后面的如果补了继续更新。