传送门:
Problem A
温暖的签到题,用个map记录一下就行了。
#include <bits/stdc++.h>
using namespace std;
string str[20];
map<string,int>mp;
int main()
{
//sfreopen("in.txt","r",stdin);
//1 chui 0 xi
mp["5--"]=1;
mp["2-"]=0;
mp["1-"]=1;
mp["4-"]=0;
mp["3-"]=1;
mp["6-"]=0;
mp["5-"]=1;
mp["7-"]=0;
mp["1"]=1;
mp["2"]=0;
mp["3"]=1;
mp["4"]=0;
mp["5"]=1;
mp["6"]=0;
mp["1+"]=1;
mp["7"]=0;
mp["3+"]=1;
mp["2+"]=0;
mp["5+"]=1;
mp["4+"]=0;
mp["1++"]=1;
mp["6+"]=0;
mp["3++"]=1;
mp["7+"]=0;
mp["0"]=-1;
int n;
cin>>n;
for(int i=1;i<=n;i++){
string str;
cin>>str;
if(mp[str]==-1) printf("X");
else if(mp[str]==1) printf("E");
else printf("I");
}
return 0;
}
Problem B
题意:
给你 m m m个串,现在让你构建一个长度为 n n n的 s t r str str串,使得这 m m m个串的都不是 s t r str str的子串。
分析:
考虑AC自动机。
将所给的 m m m个串建立AC自动机,之后我们可以考虑的一个算法是,对现在所形成的Tire图进行dfs遍历,倘若发现我们能够在这张Trie图上走 n n n步,则我们就将这 n n n步的结果输出即可。
但是因为在这里,状态数达到了 2 6 m 26^m 26m之多,如果之间对Tire图进行遍历必然会超时,故现在考虑优化。
我们发现在我们形成的Trie图中,显然是存在大量的有效环,即存在不经过 E n d End End结点的环,而倘若遇到一个这样的环,则我们只需要直接不断经过这个环即可。如此我们就可以避免遍历大量的无效的状态。
因此我们只需要预先进行预处理,我们首先先对Trie图进行遍历,记录一下有哪些结点是能够形成环,最后我们只需要在统计答案的时候优先通过这些的结点即可。
代码:
#include <bits/stdc++.h>
#define maxn 500005
using namespace std;
char str[maxn];
int n,m;
struct Trie{
int next[maxn][26],End[maxn],root,fail[maxn],id;
int vis[maxn],num[maxn],ans[maxn];
int newnode(){
for(int i=0;i<26;i++){
next[id][i]=-1;
}
End[id]=0,vis[id]=0,num[id]=0;
return id++;
}
void init(){
id=0;
root=newnode();
}
void Insert(char *str){
int len=strlen(str);
int now=root;
for(int i=0;i<len;i++){
if(next[now][str[i]-'a']==-1)
next[now][str[i]-'a']=newnode();
now=next[now][str[i]-'a'];
}
End[now]++;
}
void build(){
queue<int>que;
int now=root;
fail[root]=root;
for(int i=0;i<26;i++){
if(next[root][i]==-1){
next[root][i]=root;
}
else{
fail[next[root][i]]=root;
que.push(next[root][i]);
}
}
while(!que.empty()){
now=que.front();
que.pop();
End[now]|=End[fail[now]];
for(int i=0;i<26;i++){
if(next[now][i]==-1)
next[now][i]=next[fail[now]][i];
else{
fail[next[now][i]]=next[fail[now]][i];
que.push(next[now][i]);
}
}
}
}
void dfs1(int now){//预处理
vis[now]=1;
for(int i=0;i<26;i++){
if(vis[next[now][i]]==0&&next[now][i]!=root){
if(End[next[now][i]]) continue;
dfs1(next[now][i]);
num[now]|=num[next[now][i]];
}
else num[now]=1;
}
}
int dfs2(int step,int now,int fa){
if(fa!=-1) ans[step]=fa;
if(step==n) return 1;
for(int i=0;i<26;i++){
if((!End[next[now][i]]||next[now][i]==root)&&num[next[now][i]]!=0){
if(dfs2(step+1,next[now][i],i)) return 1;
}
}
return 0;
}
}ac;
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
ac.init();
for(int i=0;i<m;i++){
scanf("%s",str);
ac.Insert(str);
}
ac.build();
ac.dfs1(0);
ac.dfs2(0,ac.root,-1);
for(int i=1;i<=n;i++){
printf("%c",ac.ans[i]+'a');
}
return 0;
}
Problem C
题意:
有 n n n个怪兽,第 i i i个怪兽的血量为 d i d_i di,攻击力为 a i a_i ai,你的攻击力为 k k k,你现在有 c c c个力量果实,每吃一个力量果实都可以让你攻击力增加 k k k,问你最少扣多少血。
分析:
考虑贪心,显然的贪心策略是优先嗑药秒掉那些攻击力大且血比较厚的怪物。
代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
int n,k,c;
struct Node{
ll d1,x1;
bool operator <(const Node&b)const{
return x1>b.x1;
}
}q[maxn];
int main()
{
scanf("%d%d%d",&n,&k,&c);
for(int i=1;i<=n;i++){
scanf("%lld%lld",&q[i].d1,&q[i].x1);
q[i].d1=(q[i].d1+k-1)/k-1;
}
sort(q+1,q+1+n);
ll res=0;
for(int i=1;i<=n;i++){
if(q[i].d1<=c){
c-=q[i].d1;
continue;
}
else{
res+=(q[i].d1-c)*q[i].x1;
c=0;
}
}
printf("%lld\n",res);
return 0;
}
Problem D
Problem E
题意:
有一个大小为 n n n个排列,现在你你有若干个栈以及3中操作:
- 取出序列当前的第一个数字,插入到第 p p p 个栈的顶部
- 取出第 p p p 个栈的顶部数字,插入到新序列的末尾位置
- 取出第 p p p 个栈的顶部数字,插入到第 q q q 个栈的顶部
问你最少花费多少个栈使得原排列变成升序。
分析:
不难看出可以我们最多用 2 2 2个栈就可以完成上述操作。
而当且仅当形成 a i − 1 a i − 2 … a 1 a n … a i − 2 a_{i-1}~a_{i-2}\dots~a_1~a_{n}\dots a_{i-2} ai−1 ai−2… a1 an…ai−2这样的序列才能用 1 1 1个栈完成。
而上述的过程,我们只需要用一个单调栈去维护即可。
代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
int a[maxn];
int main()
{
//freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
stack<int>st;
while(!st.empty()) st.pop();
int minn=1;
bool vis=true;
for(int i=1;i<=n;i++){
if(st.empty()){
st.push(a[i]);
continue;
}
while(!st.empty()&&st.top()<a[i]){
if(st.top()!=minn){
vis=false;
break;
}
minn++,st.pop();
}
if(!vis) break;
st.push(a[i]);
}
while(!st.empty()){
if(!vis) break;
if(st.top()!=minn){
vis=false;
break;
}
minn++,st.pop();
}
if(!vis) puts("2");
else puts("1");
}
return 0;
}
Problem F
温暖的签到题,找一下规律就没了
代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long ll;
ll n,m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
cin>>n>>m;
ll ans=0;
for (int i=1;i<=min(n,m);i++)
ans+=(n-i+1)*(m-i+1)*i;
cout<<ans<<endl;
}
return 0;
}
Problem G
Problem H
解一下方程,最后注意可能会产生增根,特判一下即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<ll>ans;
ll a,b;
bool check(ll x){
ll tmp=a*x;
if (tmp<0) return 0;
ll c=sqrt(tmp);
if (c*c!=tmp) return 0;
if (c+b==x) return 1;
return 0;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while (t--){
cin>>a>>b;
ll tmp=a*a+4*a*b;
ll c=sqrt(tmp);
if (c*c==tmp) {
ll x1=a+2*b-c;
ll x2=a+2*b+c;
ans.clear();
if (x1%2==0&&x1!=x2){
x1/=2;
if (check(x1)) ans.push_back(x1);
}
if (x2%2==0){
x2/=2;
if (check(x2)) ans.push_back(x2);
}
sort(ans.begin(),ans.end());
cout<<ans.size()<<endl;
cout<<ans[0];
for (int i=1;i<ans.size();i++) cout<<' '<<ans[i];
cout<<endl;
}
}
return 0;
}
Problem I
Problem J
Problem K
签到题,模拟一下求导过程即可
代码:
#include <bits/stdc++.h>
using namespace std;
int n,k;
const int mod=998244353;
typedef long long ll;
ll a[105];
ll Ans(ll x){
ll res=1;
for(int i=x,j=1;i>=1&&j<=k;i--,j++){
res=res*i%mod;
}
return res;
}
vector<ll>res;
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=k;i++) res.push_back(0);
for(int i=n;i>=1;i--){
scanf("%lld",&a[i]);
if(i<k) continue;
else res.push_back((Ans(i)*a[i])%mod);
}
for(int i=0;i<res.size();i++){
if(i==0) printf("%lld",res[i]);
else printf(" %lld",res[i]);
}
return 0;
}