A Common Subsequence
只要找到有一个相同的元素输出即可。
AC代码:
const int N = 1010;
int a[N], b[N];
int ans;
int cnt[N];
int main()
{
int t;
sd(t);
while (t--)
{
int n, m, k;
bool flag = 0;
sdd(n, m);
mem(cnt, 0);
rep(i, 1, n)
{
sd(a[i]);
cnt[a[i]]++;
}
rep(i, 1, m)
{
sd(b[i]);
if (cnt[b[i]])
{
flag = 1;
k = 1;
ans = b[i];
}
}
if (flag)
{
puts("YES");
pdd(k, ans);
}
else
puts("NO");
}
return 0;
}
B Sequential Nim
题意:
有 N N N 个石子堆,每个石子堆有 a [ i ] a[i] a[i] 个石子,现在从第一堆开始往后取,谁最先没法取石子,谁就输了,问最后谁先手还是后手赢了?
这个博弈明显的先手必胜态,因为先手拿的话可以每次都把每堆拿剩下一个,然后下一个拿只能拿最后一个,然后到了最后一堆直接拿完。
考虑一个特殊情况那就是中间出现 1 1 1 的情况,比如:
. . . 4 , 5 , 1 , 3 ...4,5,1,3 ...4,5,1,3这种情况那就在 4 4 4 这一堆拿三个,然后在 5 5 5 这一堆拿 5 5 5 个,然后后手只能拿 1 1 1 拿完,这样先手下一次就可以直接拿完,还是先手必胜态。
考虑开头连续 1 1 1 的个数,如果奇数个 1 1 1 那么先手变后手,后手变先手。
再对全为 1 1 1 进行特判。
AC代码:
int main()
{
int t;
sd(t);
while (t--)
{
int n, m, k;
int cnt = 0;
sd(n);
rep(i, 1, n)
sd(a[i]);
rep(i, 1, n)
{
if (a[i] == 1)
cnt++;
else
break;
}
if (cnt == n)
{
if (cnt & 1)
puts("First");
else
puts("Second");
}
else
{
if (cnt & 1)
puts("Second");
else
puts("First");
}
}
return 0;
}
C Prefix Flip
题意:
一种操作,可以选择前缀长度为
k
k
k ,翻转前
k
k
k 的
01
01
01 值然后顺序再倒一下。要求在
2
n
2n
2n 步内把给定
a
a
a 串变成
b
b
b 串。
输出任意一种方案数。
既然是任意一种那就找罪容易的一种,每一个都操作,从最后一位往前开始考虑,如果 b b b 串这个位置和 a a a 串 的第一位相同那么就把 a a a 串 的第一位个翻转一下,然后再把当前位置的前缀都翻转,这样这一位就符合了。为了降低复杂度直接记录, a 1 a_1 a1在原序列中跳跃是这样的 p o s = : 1 , n , 2 , n − 1 , 3 … pos=: 1 ,n ,2, n-1, 3 … pos=:1,n,2,n−1,3…一直到 n 2 \frac{n}{2} 2n。
举个例子:
1
,
2
,
3
,
4
,
5
1,2,3,4,5
1,2,3,4,5
5 , 4 , 3 , 2 , 1 5,4,3,2,1 5,4,3,2,1
2 , 3 , 4 , 5 , 1 2,3,4,5,1 2,3,4,5,1
4 , 3 , 2 , 5 , 1 4,3,2,5,1 4,3,2,5,1
3 , 4 , 2 , 5 , 1 3,4,2,5,1 3,4,2,5,1
AC代码:
const int N = 1e5 + 50;
int n;
char s1[N], s2[N];
int cnt, res, pos;
vector<int> v;
int main()
{
int t;
sd(t);
while (t--)
{
sd(n);
ss(s1 + 1);
ss(s2 + 1);
v.clear();
cnt = n;
res = 0;
pos = 1;
per(i, n, 1)
{
//pddd(s2[i] - '0',res,(s2[i] - '0') ^ res);
if ((s1[pos] - '0') == ((s2[i] - '0') ^ res)) //每次都翻转前缀和为 n-- 的 这个操作就是代替了翻转的操作
v.pb(1);
v.pb(cnt--);
pos = n - pos + 1;//翻转后的位置.
if (pos <= n / 2)
pos++;
res = 1 - res; //0,1切换
}
printf("%d ", v.size());
for (auto i : v)
printf("%d ", i);
printf("\n");
}
return 0;
}
D Unmerge
题意:
给两个数组的 m e r g merg merg 规则,给一个 2 n 2n 2n 的序列 p p p ,问是不是两个数组 m e r g e merge merge 之后的结果。 这个 m e r g e merge merge 规则举个例子:
a = 3 , 2 , 8 , 4 a=3,2,8,4 a=3,2,8,4
b = 6 , 1 , 5 , 7 b=6,1,5,7 b=6,1,5,7
a 1 < b 1 a_1<b_1 a1<b1 所以先取 a 1 , 3 a_1,3 a1,3
a 2 < b 1 a_2<b_1 a2<b1 取 a 2 , 2 a_2,2 a2,2
a 3 > b 1 a_3>b_1 a3>b1 取 b 1 , 6 b_1,6 b1,6
a 3 > b 2 a_3>b_2 a3>b2 取 b 2 , 1 b_2,1 b2,1
a 3 > b 3 a_3>b_3 a3>b3 取 b 3 , 5 b_3,5 b3,5
a 3 > b 4 a_3>b_4 a3>b4 取 b 4 , 7 b_4,7 b4,7
最后取 a 3 , a 4 , 8 , 4 a_3,a_4,8,4 a3,a4,8,4
组成序列 32615784 3 2 6 1 5 7 8 4 32615784。
考虑第一个数 a [ 1 ] a[1] a[1],则向右第一个比 a [ 1 ] a[1] a[1] 大的数为 b [ 1 ] b[1] b[1] 。
对 b [ 1 ] b[1] b[1] 也是这样考虑,找右边第一个比他大的数,放在一块。
分组以后用 01 01 01 背包判断是否可以刚刚分组。
AC代码:
const int N = 4e5 + 50;
int n, k, m;
int a[N];
vector<int> ans;
int dp[N];
int tmp, cnt;
int main()
{
int t;
sd(t);
while (t--)
{
mem(dp, 0);
ans.clear();
sd(n);
rep(i, 1, 2 * n)
sd(a[i]);
tmp = a[1];
cnt = 1;
rep(i, 2, 2 * n)
{
if (a[i] < tmp)
cnt++;
else if (a[i] > tmp)
{
ans.pb(cnt);
cnt = 1;
tmp = a[i];
}
}
ans.pb(cnt);
sort(ans.begin(), ans.end());
rep(i, 0, ans.size() - 1)
{
per(j, n, ans[i])
dp[j] = max(dp[j], dp[j - ans[i]] + ans[i]);
}
if (dp[n] == n)
puts("YES"); //刚好装满
else
puts("NO");
}
return 0;
}