A 欧几里得
求递归n次 a+b最小的和
def gcd(a,b):
if b == 0:
return a
return gcd(b,a%b)
观察函数会发现 第n层可以从第n-1层返回来 第n层的(a,b)等于n-1层的(b,a%b)
所以n-1层的a1,b1和n层的a,b的关系是b=a1 ,b1=a%b;b的值已经确定要使和最小 就要使a最小
就是a=a1+b1,需要注意0层的b=0,这时候要使a+1。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
int t,n;
cin>>t;
while(t--)
{
cin>>n;
ll a=1,b=0;
while(n--)
{
if(b==0) b=1;
ll x=b;
b=a;
a=a+x;
}
cout<<a+b<<endl;;
}
return 0;
}
B 判断括号是否合法是栈的基础应用
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
stack<char> st;
int main()
{
string s;
cin>>s;
for(int i=0;i<s.size();i++)
{
if(st.size()==0) st.push(s[i]);
else if(st.top()=='('&&s[i]==')') st.pop();
else if(st.top()=='['&&s[i]==']') st.pop();
else if (st.top()=='{'&&s[i]=='}') st.pop();
else st.push(s[i]);
}
if(st.size()==0) puts("Yes");
else puts("No");
return 0;
}
C 求长度为k的子段最大乘积可以使用前缀和,用长度为k的区间平移求最大值就可以,因为要用前缀的积除另一个前缀的积而且要取模 就得用到逆元了,并且模数是一个素数用费马小定理比较好算,如果模数不是素数就必须要用拓展欧几里得了 还有一点需要注意如果子段中有0就不满足题意了,需要把有零的子段删去。那怎么判断呢,我用了一个前缀和,把非零的数当做1,0还是0求一遍前缀和 如果子段和不等于k 那么子段中一定有0 continue就可以了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200010,mod=998244353;
int n,k,s[N],sum[N];
ll mul[N];
ll ksm(ll a,ll b)
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%mod;
a=a*a%mod;
b/=2;
}
return ans;
}
int main()
{
cin>>n>>k;
mul[0]=1;
for(int i=1;i<=n;i++)
{
cin>>s[i];
sum[i]=sum[i-1];
if(s[i])
{
sum[i]++;
mul[i]=(mul[i-1]*s[i])%mod;
}
else mul[i]=1;
}
ll ans=0;
for(int i=k;i<=n;i++)
{
ll res=ksm(mul[i-k],mod-2)%mod;
if((mul[i]*res%mod)>ans&&sum[i]-sum[i-k]==k) ans=mul[i]*res%mod;
}
cout<<ans<<endl;
return 0;
}
D 异或结果等0需要两个数相等
打表可以发现如果区间[l,r]异或等0,就会有[1,l-1]和[1,r] 的异或结果相等
所以只需要预处理所有的前缀异或结果并统计结果的个数,如果区间[1,r]等0,那么就需要预处理一个0.
注意别爆int!!!血得教训啊,牛客还不显示过了几个样例,又越改错越多,太菜了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010,mod=998244353;
int n,k;
ll s[N],sum[N];
map<ll,ll> mp,mp1;
int main()
{
cin>>n;
ll ans=0,num0=0;
mp[0]=1;
for(int i=1;i<=n;i++)
{
cin>>s[i];
sum[i]=sum[i-1]^s[i];
mp[sum[i]]++;
}
for(auto it:mp)
{
ll y=it.second;
ans+=((y-1)*y/2);
}
cout<<ans;
return 0;
}```
E 假设有n个加号,就需要有n+1个数字,怎么使表达式最小呢,使各个数的数位尽量平均,还有高位数越小越好,预先统计各个数的数量往这几个数里面添加就行了,最后高精度计算就行了(复制粘贴真好..)
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=500010,mod=998244353;
int n,k;
string s;
int num[10],cnt;
vector<int> ve[N],ans;
vector<int> add(vector<int> &A, vector<int> &B)
{
for(int i=0;i<(B.size()+1)/2;i++)
swap(B[i],B[B.size()-i-1]);
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i ++ )
{
t += A[i];
if (i < B.size()) t += B[i];
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);
return C;
}
int main()
{
cin>>s;
for(int i=0;i<s.size();i++)
if(s[i]=='+') cnt++;
else num[s[i]-'0']++;
int sum=s.size()-cnt;
int i=0,j=1,k=0;
while(sum--)
{
while(!num[j]) j++;
if(j>=10) break;
ve[i].push_back(j);
num[j]--;
i=(i+1)%(cnt+1);
}
for(int i=ve[0].size()-1;i>=0;i--)
ans.push_back(ve[0][i]);
for(int i=1;i<=cnt;i++)
ans=add(ans,ve[i]);
for(int i=ans.size()-1;i>=0;i--)
cout<<ans[i];
return 0;
}
F 首先这是一颗树,只会有一个连通块…想太多了
首先,注意到输掉的唯一方法是自己的唯一一条边有另一个人,那么自己一定是在叶子上。
令两个人之间的距离为D。请注意,每人行动后D增加1或减少1。 因此,每有人走一步D的奇偶性都会改变。
假设最初,在牛牛移动之前,D是偶数。 那么当牛牛移动时D将始终为偶数,而当牛妹移动时D将始终为奇数。
请注意,只要牛牛和牛妹的令牌位于相邻的节点中,D=1。因此,如果D为偶数,则他们不在相邻的单元格中,并且牛牛始终可以移动。
另一方面,由于牛牛总是可以移动,因此他可以向牛妹的方向移动,将牛妹必然会最终移动到叶子上。同样,如果最初D是奇数,则牛妹获胜。
因此,答案取决于距离的奇偶性:如果是偶数,则牛牛获胜,否则牛妹获胜。
可以发现,只有牛牛的初始位置和牛妹的初始位置距离为偶数时,牛牛获胜。只需要分别求出深度为奇数的点和深度为偶数的点的数量即可。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000010,mod=998244353;
int n,k;
int s[N],dep[N];
ll cnt0,cnt1;
map<int,int> mp,mp1;
int main()
{
cin>>n;
dep[1]=1;
cnt1++;
for(int i=2;i<=n;i++)
{
cin>>s[i];
dep[i]=dep[s[i]]+1;
if(dep[i]&1)cnt1++;
else cnt0++;
}
cout<<(cnt0-1)*cnt0+(cnt1-1)*cnt1<<endl;
return 0;
}