A.古堡中的勇者
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,a,b,c,x,ans=0;
vector<int>v;
map<int,int>mp;
cin>>a>>b>>c>>n;
for(int i=1;i<=n;i++)
{
cin>>x;
mp[x]++;
}
for(auto it:mp)
if(it.first>b&&it.first<c) ans+=it.second;
cout<<ans;
}
B.三星五费
至少要留下五个金币,然后一直刷新,直到用完金币为止。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,cnt=0;
double ans=0;
cin>>n;
n-=5;
while(n>1)
{
double x=0.03;
for(int i=1;i<=cnt;i++) x*=0.97;
ans+=x;
cnt++;
n-=2;
}
printf("%.3lf",ans);
}
C.我就要不协调
每个位置需要做的次数算一下,然后取最小值。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int t,n,a[510];
cin>>t;
while(t--)
{
int ans=1e9;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=2;i<=n;i++)
{
if(a[i]<a[i-1]) ans=0;
else
{
int x=(a[i]-a[i-1])/2+1;
ans=min(ans,x);
}
}
cout<<ans<<endl;
}
}
D.古希腊掌管原神的神
2019ICPC南京站H题,题面比较不明所以。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int a,b,c;
cin>>a>>b>>c;
if(a>b+c)
{
if(a==1) cout<<"YES\n0";
else cout<<"YES\n"<<2*(b+c)+1;
}
else cout<<"NO";
}
E.字母匹配
对每种大小写字母贪心的使用次数,直到次数用完为止。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,k,x[26],y[26],ans=0;
memset(x,0,sizeof(x));
memset(y,0,sizeof(y));
string s;
cin>>n>>k;
cin>>s;
for(int i=0;i<n;i++)
if(s[i]>='A'&&s[i]<='Z') x[s[i]-'A']++;
else y[s[i]-'a']++;
for(int i=0;i<26;i++)
{
ans+=min(x[i],y[i]);
int z=abs(x[i]-y[i])/2;
if(k)
{
if(k>=z) k-=z,ans+=z;
else ans+=k,k=0;
}
}
cout<<ans;
}
F.数学家的四不要
偶数不必多说,判断质数的时间复杂度是,注意到卡特兰数增长很快,直接打表出哪些是卡特兰数即可。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,st[100010]={0};
st[1]=st[5]=st[429]=1;
cin>>n;
for(int i=1;i<=n;i++)
if(i%2==0) st[i]=1;
for(int i=2;i<=n;i++)
{
int flag=1;
for(int j=2;j<=sqrt(i)+1;j++)
if(i%j==0)
{
flag=0;
break;
}
if(flag) st[i]=1;
}
int cnt=1;
while(cnt*(cnt+1)/2<=n)
{
st[cnt*(cnt+1)/2]=1;
cnt++;
}
int ans=0;
for(int i=1;i<=n;i++)
if(!st[i]) ans++;
cout<<ans;
}
G.旗鼓相当的对手
动态规划,设dp[i][j]表示枚举到第i个物品相差为j的方案可行性,由于空间会爆炸,需要滚动数组。
#include<bits/stdc++.h>
using namespace std;
bool dp[2][200010];
int main()
{
int n,a[510],ans=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
dp[0][0]=true;
for(int i=1;i<=n;i++)
for(int j=0;j<=n*500;j++)
dp[i&1][j]=dp[i&1^1][abs(j-a[i])]|dp[i&1^1][j+a[i]];
for(int i=0;i<=500*n;i++)
if(dp[n&1][i])
{
ans=i;
break;
}
cout<<ans;
}
H.卷王
贪心,明显要把下次的最高分分配给小D,然后对于排在自己前面的人,优先把下次最少的分数分配给最靠前的人。如果这样还不能让其排名在自己后面,那不如趁机把下次最多的分数分配给他,以减少后面的人超过自己的机会。
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,k,a[200010],b[200010];
cin>>n>>k;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
int sum=a[k]+b[1],l=2,r=n,ans=1;
for(int i=1;i<k;i++)
{
if(i==k) continue;
if(a[i]+b[r]>sum) l++,ans++;
else r--;
}
cout<<ans;
}
关于本题的HACK数据:H题数据太弱,有问题_牛客网 (nowcoder.com)
I.子串比较
二分哈希。询问的原串是始终不变的,可以先对原串进行字符串哈希,然后对子串比较时,可以二分哈希值以找到两个子串第一次失配的位置,然后对这一位进行比较即可。时间复杂度最高为。暴力解法可以在牛客上通过,比如下面这个:
#include<bits/stdc++.h>
using namespace std;
int main()
{
int n,m,q;
string s,t;
cin>>n>>m>>q;
cin>>s>>t;
s=" "+s,t=" "+t;
while(q--)
{
int l1,l2,r1,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
string x=s.substr(l1,r1-l1+1),y=t.substr(l2,r2-l2+1);
if(x<y) printf("<\n");
else if(x==y) printf("=\n");
else printf(">\n");
}
}
可见牛客神机不是吹的,二分哈希方法如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=400010,P=131;
const ll mod=1e9+7;
ull p[N],x[N],y[N];
bool judge(int l1,int r1,int l2,int r2)
{
if(x[r1]-x[l1-1]*p[r1-l1+1]==y[r2]-y[l2-1]*p[r2-l2+1]) return true;
return false;
}
int main()
{
int n,m,q;
string s,t;
cin>>n>>m>>q;
cin>>s>>t;
p[0]=1,x[0]=0,y[0]=0;
s=" "+s,t=" "+t;
for(int i=1;i<=n;i++)
{
p[i]=p[i-1]*P;
x[i]=x[i-1]*P+s[i];
}
for(int i=1;i<=m;i++) y[i]=y[i-1]*P+t[i];
while(q--)
{
int l1,l2,r1,r2;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(judge(l1,r1,l2,r2)) printf("=\n");
else
{
int l=0,r=m-1,ans=0;
while(l<=r)
{
int mid=l+r>>1;
if(judge(l1,l1+mid,l2,l2+mid)) l=mid+1,ans=mid+1;
else r=mid-1;
}
if(s[l1+ans]>t[l2+ans]) printf(">\n");
else printf("<\n");
}
}
}
J.小C的学习计划
状压dp,用dp[i]表示二进制状态为i时距离学完还剩的期望步数。如果此时已经满足了重要程度大于等于m,明显dp[i]=0。否则进行推导,状态转移方程为 (c为i中二进制1的个数),移项之后即可求解dp[i]。(
很明显是因为里面有c个1,这些都是学过的,因此学了等于白学;然后
就是在原来i二进制位0的位置学了一次,相当于“定向学习”,因此是
而不是
。实际上
也只比
在二进制表示上多了一个1,因为每次只能学一科)
状态不能设置为0到i的期望步数,因为这样无法消除状态之间的影响,状态图会成环。
#include<bits/stdc++.h>
using namespace std;
int n,m,a[25];
double dp[1<<20];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=(1<<n)-1;i>=0;i--)
{
int sum=0,cnt=0;
for(int j=0;j<n;j++)
if(i>>j&1) sum+=a[n-j],cnt++;
if(sum>=m) dp[i]=0;
else
{
for(int j=0;j<n;j++)
if(!(i>>j&1)) dp[i]+=1.0/(n-cnt)*dp[i|(1<<j)];
dp[i]+=1.0*n/(n-cnt);
}
}
printf("%.6lf",dp[0]);
}
K.同学聚会
gcd满足单调性,用线段树或者ST表维护区间gcd值,然后二分gcd不同的点,把gcd相同的区间答案合并计算,可以发现最多二分次,总复杂度是
级别的。
对于的做法,我们可以固定左端点,然后用vector存储区间gcd的值以及该值对应子数组右端点的最大值,对于每段相同gcd的区间计算贡献。可以发现gcd相同的区间的长度是公差为1的等差数列,直接
计算每个gcd的贡献即可。由于gcd最多变化log次,因此总时间复杂度为
。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=998244353,inv=499122177;
ll fastpow(ll a,ll k)
{
ll res=1;
while(k)
{
if(k&1) res=res*a%mod;
k>>=1;
a=a*a%mod;
}
return res;
}
int main()
{
ll n,a[200010],ans=0;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
vector<pair<ll,int>>v;
for(int i=n;i;i--)
{
v.push_back({a[i],i});
v[0].first=__gcd(v[0].first,a[i]);
int k=0;
for(int j=1;j<v.size();j++)
{
v[j].first=__gcd(v[j].first,a[i]);
if(v[j].first==v[k].first) v[j].second=v[k].second;
else v[++k]=v[j];
}
v.resize(k+1);
for(int j=1;j<v.size();j++)
{
ll x=v[j-1].first,y=v[j].second-i+2,z=v[j-1].second-i+1,num=v[j-1].second-v[j].second;
ans=(ans+(ll)(y+z)*num%mod*inv%mod*x%mod)%mod;
}
ll x=v[v.size()-1].first,y=1,z=v[v.size()-1].second-i+1,num=v[v.size()-1].second-i+1;
ans=(ans+(ll)(y+z)*num%mod*inv%mod*x%mod)%mod;
}
ll C=n*(n+1)%mod*inv%mod;
ll sum=fastpow(C,mod-2);
cout<<ans*sum%mod;
}
L.删边游戏
tarjan和树形dp?后面的回头再写
M.网格谜题
构造+大模拟+启发式合并
群里有后面几道题的题解