A题
- 题意
一个字符串只包含A,B,C;你可以把同样的字母换成’(‘或者’)’.。问存不存在一种状态,使得字符串称为括号匹配的字符串。
- 思路
官方思路:先去判断首尾,如果相同,则输出”NO“;然后继续判断。如果第一个字母的数量在整个串中是一般的话,那么另外两个字母必须都是反括号;否则另外一个字母得是正括号。因为只有三个数1、2、3,所以知道其中两个可以利用 3^ x^y获得。然后判断是否是合法的匹配括号时,利用前缀和(正括号记为1,反括号记为-1),看是否过程中有小于0的情况出现,或者最后不等于0. - 代码
#include <bits/stdc++.h>
using namespace std;
bool solve() {
string s;
cin >> s;
vector<int> d(3);
int x = s[0] - 'A';
int y = s.back() - 'A';
if (x == y)
return false;
d[x] = 1; d[y] = -1;
if (count(s.begin(), s.end(), 'A' + x) == s.length() / 2)
d[3 ^ x ^ y] = -1;
else
d[3 ^ x ^ y] = 1;
int bal = 0;
for (char c : s) {
bal += d[c - 'A'];
if (bal < 0) return false;
}
return bal == 0;
}
int main() {
int t; cin >> t;
while (t--) {
cout << (solve() ? "YES\n" : "NO\n");
}
}
B题
- 题意
给你 n ∗ n n * n n∗n 的矩阵,原来都是白色的格子,然后给你URLD分别代表最边上一列/行,看看能否实现。
- 思路
本蒟蒻的思路是枚举不符合情况的情况,wa了5、6次才过。主要就是考虑0,1,n-1,n这四种情况。 - 蒟蒻代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
int a[N],cnt[N];
#define PII pair<int,int>
#define x first
#define y second
#define PB push_backec
void solve()
{
int n;int p[5];
cin>>n;
for(int i=1;i<=4;i++) cin>>p[i];
p[0]=p[4];
for(int i=1;i<=4;i++){
if(p[i]==n){
if(p[(i+1)%4]==0||p[(i-1+4)%4]==0){
cout<<"NO"<<endl;
return;
}
}
}
for(int i=1;i<=4;i++){
if(p[i]==n-1){
if(p[(i+1)%4]==0&&p[(i-1+4)%4]==0){
cout<<"NO"<<endl;
return;
}
}
}
for(int i=1;i<=4;i++){
if(p[i]==1){
if(p[(i+1)%4]==n&&p[(i-1+4)%4]==n){
cout<<"NO"<<endl;
return;
}
}
}
for(int i=1;i<=4;i++){
if(p[i]==0){
if(p[(i+1)%4]>=n-1&&p[(i-1+4)%4]>=n-1&&p[(i+2)%4]==1){
cout<<"NO"<<endl;
return;
}
if(p[(i+1)%4]>=n-1&&p[(i-1+4)%4]>=n-1&&p[(i+2)%4]==0){
cout<<"NO"<<endl;
return;
}
}
}
for(int i=1;i<=4;i++){
if(p[i]==1){
if(p[(i+1)%4]==n-1&&p[(i-1+4)%4]==n&&p[(i+2)%4]==1){
cout<<"NO"<<endl;
return;
}
if(p[(i+1)%4]==n&&p[(i-1+4)%4]==n-1&&p[(i+2)%4]==1){
cout<<"NO"<<endl;
return;
}
}
}
cout<<"YES"<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
- 官方思路
枚举四个角的情况,放与不放,然后放的话,就是两边各贡献一个1. - 官方python代码
for _ in range(int(input())):
n, U, R, D, L = map(int, input().split())
for mask in range(16):
rU, rR, rD, rL = U, R, D, L
if mask & 1:
rU -= 1
rL -= 1
if mask & 2:
rL -= 1
rD -= 1
if mask & 4:
rD -= 1
rR -= 1
if mask & 8:
rR -= 1
rU -= 1
if min(rU, rR, rD, rL) >= 0 and max(rU, rR, rD, rL) <= n - 2:
print("YES")
break
else:
print("NO")
C题
- 题意
给你n个箱子,然后m个特殊位置,每次推动箱子,注意不能跨过箱子,所以就和推箱子游戏是一样的,问你最多能够使得多少个箱子在特殊位置。
注意一开始处于位置0,所以推箱子是累积的。
-
思路
-
代码
#include <bits/stdc++.h>
#define forn(i, n) for (int i = 0; i < int(n); i++)
using namespace std;
int calc(const vector<int> &a, const vector<int> &b){
int n = a.size();
int m = b.size();
vector<int> c(n), res;
vector<int> su(n + 1);
int r = m - 1;
for (int i = n - 1; i >= 0; --i){
su[i] = su[i + 1];
while (r >= 0 && b[r] > a[i]) --r;
if (r >= 0 && b[r] == a[i]) ++su[i];
}
int ans = 0;
int j = 0;
r = 0;
forn(l, m){
while (j < n && a[j] <= b[l] + j) ++j;
while (r < m && b[r] - b[l] < j) ++r;
ans = max(ans, r - l + su[j]);
}
return ans;
}
int main() {
int t;
scanf("%d", &t);
forn(_, t){
int n, m;
scanf("%d%d", &n, &m);
vector<int> a(n), b(m);
forn(i, n) scanf("%d", &a[i]);
forn(i, m) scanf("%d", &b[i]);
vector<int> al, bl, ar, br;
forn(i, n){
if (a[i] < 0) al.push_back(-a[i]);
else ar.push_back(a[i]);
}
forn(i, m){
if (b[i] < 0) bl.push_back(-b[i]);
else br.push_back(b[i]);
}
reverse(al.begin(), al.end());
reverse(bl.begin(), bl.end());
printf("%d\n", calc(al, bl) + calc(ar, br));
}
return 0;
}
- 一位大佬做法
正数部分和负数部分分开来考虑。
对于每一个特殊位置(该位置上没有箱子),我们都尝试将箱子正好推到这个位置,这样这个特殊点前面的所有箱子会从这个点向前延伸出去,然后看这部分延伸会碰到多少特殊位置即可,最后加上特殊点上本来就有箱子的个数(可用二分来找,具体看代码)。维护一个最大值。
负数部分和正数部分相加即是答案。 - 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 200005
int n,m;
int input[N],a1[N],b1[N],a2[N],b2[N],cnt11,cnt12,cnt21,cnt22;
map<int,int> mp;
int solve(int a[],int b[],int lena,int lenb)
{
int res = 0;
mp.clear();
int mh = 0;
for(int i=1;i<=lena;i++) mp[a[i]] = 1;
for(int i=1;i<=lenb;i++) if(mp[b[i]] == 1) mh++;
res = mh;
for(int i=1;i<=lenb;i++)
{
if(mp[b[i]] == 1)
{
mh--;
continue;
}
int sum = upper_bound(a + 1, a + 1 + lena, b[i]) - a - 1;
int pos = lower_bound(b + 1, b + 1 + lenb, b[i] - sum + 1) - b;
res = max(res, mh + i - pos + 1);
}
return res;
}
int main()
{
int Case = 1;
scanf("%d",&Case);
while(Case--)
{
cnt11 = cnt12 = cnt21 = cnt22 = 0;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&input[i]);
if(input[i] > 0) a1[++cnt11] = input[i];
}
for(int i=n;i>=1;i--)
{
if(input[i] < 0) a2[++cnt12] = -1 * input[i];
}
for(int i=1;i<=m;i++)
{
scanf("%d",&input[i]);
if(input[i] > 0) b1[++cnt21] = input[i];
}
for(int i=m;i>=1;i--)
{
if(input[i] < 0) b2[++cnt22] = -1 * input[i];
}
int ans = 0;
ans += solve(a1, b1, cnt11, cnt21);
ans += solve(a2, b2, cnt12, cnt22);
printf("%d\n",ans);
}
return 0;
}
- 自己的理解
因为一开始的话,处于0位置,所以可以分开负数和正数,为什么?(因为正数不可能推到负数位置上去,负数不可能推到正数位置上去。)因为不能跨越箱子推,所以箱子是累积的,这样的话,通过枚举推到每一个特殊位置就可以二分查找出,有多少个连续的箱子位于特殊位置上;维护一个最大值,然后左右相加就是答案。
- 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10,mod=1e9+7;
int F[N],S[N];
#define PII pair<int,int>
#define x first
#define y second
#define PB push_back
int deal(vector<int> &a,vector<int> &b)
{
int n=a.size();
int m=b.size();
int sc=0;
map<int,int> mp;
for(auto &i:a){
mp[i]=1;
}
for(auto &j:b){
if(mp[j]==1) sc++;
}
int ans=sc;
for(int i=0;i<m;i++){
if(mp[b[i]]==1){
sc--;
continue;
}
int fir=upper_bound(a.begin(),a.end(),b[i])-a.begin();
int sec=lower_bound(b.begin(),b.end(),b[i]-fir+1)-b.begin();
ans=max(ans,i+1-sec+sc);
}
return ans;
}
void solve()
{
vector<int> Fl,Fr,Sl,Sr;
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>F[i];
for(int i=1;i<=m;i++) cin>>S[i];
for(int i=1;i<=n;i++) {
if(F[i]>0) Fr.push_back(F[i]);
else Fl.push_back(-F[i]);
}
for(int i=1;i<=m;i++) {
if(S[i]>0) Sr.push_back(S[i]);
else Sl.push_back(-S[i]);
}
reverse(Fl.begin(),Fl.end());
reverse(Sl.begin(),Sl.end());
int ans=0;
ans+=deal(Fl,Sl);
ans+=deal(Fr,Sr);
cout<<ans<<endl;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while (t--)
solve();
}
E题
- 题意
给你一个图,每条边都有一个字母。给你m次操作,要么增加一条边,要么去掉一条边,要么查询是否存在路径长度为k的一条路,正着走,反着走一样。 - 思路
如果k是奇数,那么只要找到一组双向边的就行。
如果k是偶数,那么需要一组双向边的权值一样。
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair<int,int>
#define pb push_back
#define mp make_pair
#define vi vector<int>
#define vll vector<ll>
const int maxn = 1e6 + 5;
const int mod = 1e9 + 7;
int a[maxn];
map<pii , char> e;
int main()
{
ios::sync_with_stdio(false);
int n , m; cin >> n >> m;
int x , y; x = y = 0;
for (int i = 1 ; i <= m ; i++){
char c; cin >> c;
int u , v;
char w;
if (c == '+'){
cin >> u >> v >> w;
e[mp(u,v)] = w;
pii g = mp(v,u);
x += (e.find(g) != e.end());
y += (e.find(g) != e.end() && e[g] == w);
}
else if (c == '-'){
cin >> u >> v;
w = e[mp(u,v)];
e.erase(mp(u,v));
pii g = mp(v,u);
x -= (e.find(g) != e.end());
y -= (e.find(g) != e.end() && e[g] == w);
}else {
//x代表图中存在几组正向反向联通的。
//y代表图中存在几组不仅正反向联通,而且权值相同的。
int k; cin >> k;
if (k & 1) cout << (x ? "YES" : "NO") << endl;
else cout << (y ? "YES" : "NO") << endl;
}
}
return 0;
}