A. Not a Substring
题意:给定一个括号字符串,要求构造出一个长度为2*n的新串,使给定字符串作为连续的子串不出现在新串中。
思路:对于新串的构造无非只有((()))和()()()这两种,一一构造出来然后检验原串是否出现即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair<ll, ll> pll;
void solve()
{
string s;
cin>>s;
string a,b;
for(int i=0;i<s.size()*2;i++){
if(i%2==0){
a.push_back('(');
}
else a.push_back(')');
}
for(int i=0;i<s.size();i++){
b.push_back('(');
}
for(int i=0;i<s.size();i++){
b.push_back(')');
}
int f1=0,f2=0;
if(a.find(s)!=-1) f1=1;
if(b.find(s)!=-1) f2=1;
if(f1&&f2){
cout<<"NO"<<endl;
}
else{
cout<<"YES"<<endl;
if(!f1) cout<<a<<endl;
else if(!f2) cout<<b<<endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
B. Fancy Coins
题意:有两种物品,一种价值为1,一种价值为k,每种物品会给定一些有限使用次数,若有限次数使用完后需要花费单位代价再次进行使用,现给定m,让两种物品的价值总和恰好为m且花费的单位代价最小(即有限次数的花费不算入单位代价中)
思路:贪心,对于给定的m,若其能整除k,那么这种是否我们只需要尽可能的使用k即可,对于价值为1的若我们有x*k个,同样相当于提供了x个k,但若m不能整除k,那么我们就让价值为1的物品去补足余数,剩下的尽可能去补k即可,若其连余数都无法补足,那么我们需要花费单位代价去用1补足余数即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair<ll, ll> pll;
void solve()
{
ll m,k,a,b;
cin>>m>>k>>a>>b;
int x=m%k;
int y=m/k;
if(a>=x){
a-=x;
a/=k;
b+=a;
cout<<max(0ll,y-b)<<endl;
}
else{
cout<<max(0ll,y-b)+x-a<<endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
C. Game on Permutation
题意:太长懒得翻译
思路:既然是博弈,那么肯定涉及到了必胜态和必败态一说,对于当前的i,若其能到达的点都为必败态,那么此时的状态即为必胜态,反之则为必败态,对于当前状态,若无法移动,则也为必败态,知道了上述条件,再结合题意,我们可以知道,我们当前点能够移动到的点为小于自己的点,那么我们根据这些小于自身的点的状态即可判断当前状态。
思考应该如何去维护上述过程:我们可以知道,树状数组的经典运用:可以维护逆序对,即树状数组可以计算出遍历到当前点时,前方有多少个点比自己小,已知这个作用,那么我们还剩一个信息没有维护,即我们怎么去统计前方比自己点小的状态。我们假定必胜态为1,必败态为0,那么我们同样可以使用树状数组去统计,因为对于当前点i,树状数组维护的是1-i的前缀和,那么我们可以知道,所有已经出现了的比自己小的点肯定在当前i的前面,那么若1-i的前缀和为 0,就代表着,前方所有点的状态都为必败态,那么当前点的状态就为必胜态,反之,若前缀和不为0,则代表着前方至少有一个点的状态为必胜态,那么当前点的状态就是必败态,我们将当前点的状态放入树状数组即可。
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
typedef long long ll;
const int maxv = 4e6 + 5;
typedef pair<ll, ll> pll;
int a[N];
struct MIT
{
ll tr[N];
int lowbit(int x) {
return x & (-x);
}
void add(int u, int v) {
for (int i = u; i < N; i += lowbit(i)) {
tr[i] += v;
}
}
ll query(int x) {
ll res = 0;
for (int i = x; i > 0; i -= lowbit(i)) {
res += tr[i];
}
return res;
}
};
MIT t1,t2;
//t1维护的是到目前为止,前方有多少个比自己小的点
//t2则是维护的比自己小的点的信息
void solve()
{
int n;
cin>>n;
int cnt=0;
memset(t1.tr,0,sizeof t1.tr);
memset(t2.tr,0,sizeof t2.tr);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
int x1=t1.query(a[i]);
int x2=t2.query(a[i]);
if(x1==0){//x1==0代表当前点前方没有点,根据题意,这种状态也为必败态
t1.add(a[i],1);//那么我们直接把它加入到t1即可
}
else if(x2==0){
t2.add(a[i],1);//如果当前点前方全为必败态,那么当前点就是必胜态
t1.add(a[i],1);
cnt++;
}
}
cout<<cnt<<endl;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
system("pause");
return 0;
}
还有一种做法可以在O(n)的时间复杂度内求出,即我们维护两个变量,一个为到目前为止的最小值x,一个为必胜态的最小值y,对于位置i,考虑当前位置在什么情况下合法,即ai必须大于x,因为得保证前方有点可以进行移动,且ai<y,因为ai若大于y,则当前状态的前驱为必胜态,一旦前面的点有必胜态存在,那么当前点即为必败态。因此统计的数量即可