T1
思路: 水题,直接模拟。注意用字符串读入更加方便。
#include <bits/stdc++.h>
#define int long long
using namespace std;
string s;
signed main()
{
cin>>s;
if (s[0]=='7'||s[1]=='7'||s[2]=='7') cout<<"Yes"<<endl;
else cout<<"No"<<endl;
return 0;
}
T2
思路: 直接模拟即可。注意答案为所有不大于n的无法被3或5整除的数之和,与其他的数(Fizz?Buzz?FizzBuzz?这不是数)无关。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,ans=0;
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
if (i%3!=0&&i%5!=0) ans+=i;
}
cout<<ans<<endl;
return 0;
}
T3
思路: 还说啥,直接模拟。gcd不用打辗转相除了,直接用个__gcd(a,b)完事。
注意NOIP中千万不要用双下划线的函数,否则CE;一定要打辗转相除,这样还会快一点!
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int k,ans=0;
int get_gcd(int a,int b)
{
if (b==0) return a;
else return get_gcd(b,a%b);
}
int gcd(int a,int b)
{
int x=a,y=b;
return get_gcd(x,y);
}
signed main()
{
cin>>k;
for (int i=1;i<=k;i++)
{
for (int j=1;j<=k;j++)
{
for (int w=1;w<=k;w++) ans+=gcd(gcd(i,j),w);
}
}
cout<<ans<<endl;
return 0;
}
本蒟蒻觉得这三题是不是太水了些,以后能不能学一学Div.3前三题的难度?
T4
考虑找到满足①的(i,j,k)的数量再减去不满足②但满足①的(i,j,k)的数量。
首先,如何找满足①的(i,j,k)呢? 不妨设i<j<k。首先,前面两重循环枚举i, j的值,然后对于(i,j)查满足要求的k的数量,并发现这一步(查k的数量)可以优化。即,这里满足要求的k的数量为区间[j+1,n]中与Si, Sj均不相同的位置数,可以用后缀和轻松搞定。
然后再算,满足要求①但不满足②的(i,j,k)只有一种情况: Si≠Sj且Sj≠Sk且j-i=k-j。那么,还是一样,两重循环枚举i, j,然后查满足要求的k的值,这里k=j+(j-i)=2j-i。注意不要让k超界。
然后把上面两个答案相减即可。具体操作请见代码。
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,ans=0;
int a[5005],pre[5][5005];
signed main()
{
cin>>n;
for (int i=1;i<=n;i++)
{
char ch;
cin>>ch;
if (ch=='R') a[i]=0;
else if (ch=='G') a[i]=1;
else a[i]=2;
}
for (int i=n;i>=1;i--)
{
pre[a[i]][i]=pre[a[i]][i+1]+1;
pre[(a[i]+1)%3][i]=pre[(a[i]+1)%3][i+1];
pre[(a[i]+2)%3][i]=pre[(a[i]+2)%3][i+1];
}
for (int i=1;i<=n-2;i++)
{
for (int j=i+1;j<=n-1;j++)
{
if (a[i]==a[j]) continue;
int pos;
for (int k=0;k<=2;k++)
{
if (k!=a[i]&&k!=a[j])
{
pos=k;
break;
}
}
ans+=pre[pos][j+1];
}
}
for (int i=1;i<=n-2;i++)
{
for (int j=i+1;j<=n-1;j++)
{
int pos=2*j-i;
if (pos>n||pos<=0) break;
if (a[i]!=a[j]&&a[i]!=a[pos]&&a[j]!=a[pos]) ans--;
}
}
cout<<ans<<endl;
return 0;
}
另外,我们班的一位大佬还有一种思路,就是把R, G, B的位置分别存并枚举R, G的位置,累加此时满足要求的B的位置的数量。个人认为这种做法更好更快,但是更容易写错。
贴上这位大佬的代码:
#include<bits/stdc++.h>
using namespace std;
vector<int> vr,vg,vb;
int main(){
string s;
int n;
long long ans=0;
cin>>n>>s;
for(int i=0;i<n;i++){
if(s[i]=='R') vr.push_back(i);
else if(s[i]=='G') vg.push_back(i);
else vb.push_back(i);
}
int a=vr.size(),b=vg.size(),c=vb.size();
for(int i=0;i<a;i++){
for(int j=0;j<b;j++){
ans+=c;
int d=abs(vr[i]-vg[j]);
int mi=min(vr[i],vg[j]),ma=max(vr[i],vg[j]);
if(mi-d>=0&&s[mi-d]=='B') ans--;
if(ma+d<n&&s[ma+d]=='B') ans--;
if((vr[i]+vg[j])%2==0&&s[mi+d/2]=='B') ans--;
}
}
cout<<ans;
return 0;
}
这是最典型的两种做法。本题做法很多,这里不再论述。
T5
赛后轻松AC,赛中脑子瓦特……
思路: 考虑dp 递推
状态设计 d p [ i ] dp[i] dp[i]表示最大公约数为i的不同序列的个数。
状态转移的第一部分为 d p [ i ] = [ k / i ] n dp[i]=[k/i]^n dp[i]=[k/i]n,即 [ k / i ] [k/i] [k/i]表示每个数取值的数量(由于最大公约数为i,那么每个数都必须是i的倍数且需要小于等于k,那么每个数就可以取 [ k / i ] [k/i] [k/i]种值,其中 [ a / b ] [a/b] [a/b]表示 a / b a/b a/b向下取整的值);由于有n个数,那么根据乘法原理得到 d p [ i ] = [ k / i ] n dp[i]=[k/i]^n dp[i]=[k/i]n这个显而易见的式子。
接着,相信各位大佬都发现了,如果一个序列的最大公约数为6,那么这种序列会被重复算,即在 d p [ 6 ] dp[6] dp[6]算了一次,又在 d p [ 3 ] , d p [ 2 ] , d p [ 1 ] dp[3],dp[2],dp[1] dp[3],dp[2],dp[1]算了一次。所以得到整个状态转移公式,即 d p [ i ] = [ k / i ] n − Σ d p [ t ] dp[i]=[k/i]^n-Σdp[t] dp[i]=[k/i]n−Σdp[t],其中t不等于i,且t为i的倍数, 也需满足 t ≤ k t≤k t≤k。
然后贴上核心代码(状态转移):
for (int i=k;i>=1;i--)
{
a[i]=quick_power(k/i,n)%mod;
for (int j=2*i;j<=k;j+=i) a[i]=((a[i]-a[j])%mod+mod)%mod;
}
时间复杂度: O ( ( l o g n + l o g k ) k ) O((logn+logk)k) O((logn+logk)k),注意这里 l o g log log省略的底数为2。
贴上高清无码 的代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,k,mod=1e9+7,ans=0;
int a[100005];
int quick_power(int a,int b)
{
int res=1;
for (;b;b=b>>1,a=(a*a)%mod)
{
if (b&1) res=(res*a)%mod;
}
return res;
}
signed main()
{
cin>>n>>k;
for (int i=k;i>=1;i--)
{
a[i]=quick_power(k/i,n)%mod;
for (int j=2*i;j<=k;j+=i) a[i]=((a[i]-a[j])%mod+mod)%mod;
}
for (int i=1;i<=k;i++) ans=(ans+a[i]*i)%mod;
cout<<ans<<endl;
return 0;
}
T6
待填坑……
总结
①排名: 1544(中国第126)
②总分: 1000
③我还是太弱了……