A. Three Decks

#1.由于最后三个数会相等,提前算出来和,%3判断,再判前两个数是否大于
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int a,b,c;cin>>a>>b>>c;
if((a+b+c)%3==0)
{
int tmp=(a+b+c)/3;
if((a<=tmp)&&(b<=tmp))cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
else cout<<"NO"<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
B. Move to the End

我们每次要从数组末尾拿1,2,3...一直到n 个数,对于每次取,我都可以拿一个数到数组末尾。
#1.为此我们可以取一个前缀max数组,讨论从后向前的k个元素的前缀max是否大于这个元素
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n;cin>>n;
vector<ll>a(n+1);
vector<ll>pre(n+1,0);
for(int i=1;i<=n;i++)
{
cin>>a[i];
pre[i]=max(pre[i-1],a[i]);
}
ll sum=0;
for(int i=n;i>=1;i--)
{
sum+=a[i];
if(pre[i-1]>a[i])cout<<sum-a[i]+pre[i-1]<<" ";
else cout<<sum<<" ";
}
cout<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
C. Card Game

有n张卡,编号大的克制编号小的,特别的1克制n,每回合Alice先出,Bob后手,如Alice克制Bob则得Alice得这两张牌,反之Bob
#1.注意到由于Alice先手,只有无论Alice出任何牌Bob都能克制她时,Bob胜,其余情况Alice胜
#2.可以用vector存Alice的牌,set存Bob的牌,二分+特判
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
void solve()
{
int n;cin>>n;
string s;cin>>s;
vector<int>a;
s="#"+s;
set<int>st;
bool tag=true;
for(int i=1;i<=n;i++)
{
if(s[i]=='A')a.push_back(i);
else st.insert(i);
}
int l=a.size();
for(int i=0;i<l;i++)
{
if(a[i]==n)
{
if(!(st.count(1)))tag=false;
}
else
{
auto it=st.upper_bound(a[i]);
if(a[i]==1)
{
if(*it==n)tag=false;
}
else
{
if(*it<a[i])tag=false;
}
}
}
if(tag)cout<<"Bob"<<endl;
else cout<<"Alice"<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
while (t--)
{
solve();
}
return 0;
}
D. Array and GCD

给一个长为n的数组a,可以让数组中的任一元素减1,另一元素加1,也可只减不加,最后问最少删几个元素能让数组任意两个元素之间互质,(删元素要在操作之前),并且要保证操作后的数组中任意元素都是>=2的
#1. 要让任意两个元素之间互素,当数组中都是素数即可
#2.再来考虑操作,我能让数组中任意元素变化,但数组的总和只能是不变或变小的,假设剩了x个元素,它最小应该是前x个素数相加。
#3.到这只需用欧拉筛筛到1e7(大概6e5个素数),在对素数做前缀和,即为保留i个元素,i个元素的总和至少为多少
#4.保持贪心性质,删数只删最小的
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e7 + 10;
const int M=1e6+4;
bool vis[N];
vector<int>primes;
ll pre[M];
void init(int x)
{
vis[0]=vis[1]=true;
for(int i=2;i<=x;i++)
{
if(!vis[i])primes.push_back(i);
int l=primes.size();
for(int j=0;j<l&&primes[j]*i<=x;j++)
{
vis[i*primes[j]]=true;
if(i%primes[j]==0)break;
}
}
}
void solve()
{
int n;cin>>n;
vector<int>a(n+1);
ll sum=0;
for(int i=1;i<=n;i++)cin>>a[i],sum+=a[i];
sort(a.begin()+1,a.end(),greater<int>());
if(n<2)
{
cout<<0<<endl;
return;
}
for(int i=n;i>=2;i--)
{
if(sum>=pre[i])
{
cout<<n-i<<endl;
return;
}
sum-=a[i];
}
cout<<n-1<<endl;
}
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;cin>>t;
init(1e7);
int l=primes.size();
//cout<<l<<endl;
for(int i=0;i<l;i++)
{
pre[i+1]=pre[i]+primes[i];
}
while (t--)
{
solve();
}
return 0;
}
E. Unpleasant Strings

给一个长为n的字符串s,其中只能出现26个字母前k个字母,q次询问,每次询问给一个字符串,对于每次询问给出至少添加几个字母能使它不为s的子序列。
#1.对于本就不是它的子序列的字符串,添加0个。那就需要判断以一个字符串是否为另一个字符串的子序列。k很小,可以将每种字符用vector存起来,在判断时,只需要遍历要判断的字符串,二分对于这个字符出现序列中第一个大于上一个位置的位置
#2.通过上述方法可以找到这个字符串作为子序列第一个末尾,对于后续元素至少要添加的元素数量,我们可以采用后缀和提前预处理出答案,后缀和是从后向前遍历,将k个元素都出现的一段视作1,再做后缀和
cpp
#include<iostream>
#include<vector>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<queue>
#include<cstring>
#include<stack>
#include<array>
#include<cmath>
#include<set>
#include<unordered_set>
#include<unordered_map>
#include<iomanip>
using namespace std;
using ll = long long;
using llu = unsigned long long;
const ll inf = 0x3f3f3f3f3f3f3f3fll;
const ll MIN = -9187201950435737472ll;
ll mod = 1e9 + 7;
ll base = 131;
const int N = 1e4 + 10;
int main()
{
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int l,q,k;cin>>l>>k;
string s;cin>>s;
string str;
vector<vector<int>>dp(k+1,vector<int>());
vector<ll>suf(l+3,0);
s="#"+s;
for(int i=1;i<=l;i++)
{
dp[s[i]-'a'+1].push_back(i);
}
suf[l]=0;
int cnt=0;
vector<bool>vis(k+1,false);
for(int i=l-1;i>=0;i--)
{
if(!vis[s[i+1]-'a'+1])cnt++,vis[s[i+1]-'a'+1]=true;
suf[i]=suf[i+1]+(cnt==k);
if(cnt==k)
{
fill(vis.begin(),vis.end(),false);
cnt=0;
}
}
cin>>q;
while(q--)
{
cin>>str;
int len=str.length();
int pos=-1;
bool tag=true;
for(int i=0;i<len;i++)
{
int x=str[i]-'a'+1;
auto tmp=upper_bound(dp[x].begin(),dp[x].end(),pos);
//cout<<tmp<<endl;
if(tmp==dp[x].end())
{
tag=false;
break;
}
pos=*tmp;
//cout<<pos<<endl;
}
if(!tag)
{
cout<<0<<endl;
continue;
}
//cout<<pos<<endl;
cout<<suf[pos]+1<<endl;
}
return 0;
}