牛客练习赛27游记
A - 纸牌
题目大意:
有两张纸牌,两张纸牌上有相同的正整数(n(nle10^9))。
每一轮一张纸牌上的数都可以减去小于等于另外一张纸牌上的数的非负数。
每一轮只能操作和上轮不同的纸牌。
求三轮之后两张纸牌上数字之和的最小值。
思路:
答案就是(lceilfrac n2 ceil)。
源代码:
#include<cstdio>
#include<cctype>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
int main() {
const int n=getint();
printf("%d
",n/2+n%2);
return 0;
}
B - 手办
题目大意:
给定(n(nle10^{12})),求(sum_{k=1}^nsum_{a=1}^ksum_{b=1}^k[ab|k])。
思路:
题目就是要求满足(abcle n)的三元组((a,b,c))个数。
若(ale ble c),那么(a)枚举到(sqrt[3]n),(b)在剩下(sqrt{frac na})中枚举,(c)的个数可以直接算出来。
交换(a,b,c)总共会有(6)种方案。
另外注意判断(2)个数和(3)个数相同的情况。
源代码:
#include<cstdio>
#include<cctype>
typedef long long int64;
inline int64 getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int64 x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int mod=2333;
int main() {
const int64 n=getint();
int ans=0;
for(register int i=1;(int64)i*i*i<=n;i++) {
(ans+=1+(n/i/i-i)*3%mod)%=mod;
for(register int j=i+1;(int64)j*j<=n/i;j++) {
(ans+=3+(n/i/j-j)*6%mod)%=mod;
}
}
printf("%d
",ans);
return 0;
}
C - 水图
题目大意:
一棵(n(nle50000))个结点的带边权的树,求从(x)出发经过每个点至少一次最少需要走多少路。
思路:
所有边权和×2-从x出发能走最远的距离。
源代码:
#include<cstdio>
#include<cctype>
#include<vector>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=50001;
typedef long long int64;
int64 dep[N],max;
struct Edge {
int to,w;
};
std::vector<Edge> e[N];
inline void add_edge(const int &u,const int &v,const int &w) {
e[u].push_back((Edge){v,w});
e[v].push_back((Edge){u,w});
}
void dfs(const int &x,const int &par) {
for(auto &j:e[x]) {
const int &y=j.to,&w=j.w;
if(y==par) continue;
dep[y]=dep[x]+w;
dfs(y,x);
}
max=std::max(max,dep[x]);
}
int main() {
const int n=getint(),x=getint();
int64 ans=0;
for(register int i=1;i<n;i++) {
const int u=getint(),v=getint(),w=getint();
add_edge(u,v,w);
ans+=(int64)w*2;
}
dfs(x,0);
ans-=max;
printf("%lld
",ans);
return 0;
}
D - 愤怒
题目大意:
将一个长度为(n(nle10000))的序列划分为恰好两个子序列,使得两个子序列合法,求方案数。
思路:
左括号当成(1),右括号当(-1)。(f_{i,j})表示考虑完前(i)个字符,第一个序列的前缀和是(j)的方案数。考虑下一个字符时,可以将其加入第一个序列和第二个序列。如果前缀和(<0)就不合法,否则就可以转移。
时间复杂度(mathcal O(n^2))。
源代码:
#include<cstdio>
#include<cctype>
#include<cstring>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
inline bool check(const char &ch) {
return ch=='('||ch==')';
}
inline int getval() {
register char ch;
while(!check(ch=getchar()));
if(ch=='(') return 1;
if(ch==')') return -1;
return 0;
}
const int N=10001,mod=2333;
int f[2][N],sum[N];
int main() {
const int n=getint();
f[0][0]=1;
for(register int i=1;i<=n;i++) {
const int cur=i&1;
const int x=getval();
sum[i]=sum[i-1]+x;
if(sum[i]<0) {
puts("0");
return 0;
}
memset(f[cur],0,sizeof f[cur]);
for(register int j=0;j<=n;j++) {
if(0<=j+x&&j+x<=n) {
(f[cur][j+x]+=f[!cur][j])%=mod;
}
if(sum[i]-j>=0) {
(f[cur][j]+=f[!cur][j])%=mod;
}
}
}
printf("%d
",f[n&1][0]);
return 0;
}
E - 欧拉
题目大意:
(m(mle5 imes10^5))次询问,每次给出(n(nle5 imes10^6)),求(F(n)=sum_{d|n}d^kmu(frac nd))。模(998244353)。
思路:
(F(n))是一个积性函数,直接线性筛即可。
源代码:
#include<cstdio>
#include<cctype>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
typedef long long int64;
const int N=5e6+1,mod=998244353;
int m,k;
inline int power(int a,int k) {
int ret=1;
for(;k;k>>=1) {
if(k&1) ret=(int64)ret*a%mod;
a=(int64)a*a%mod;
}
return ret;
}
int p[N],f[N];
bool vis[N];
inline void sieve() {
for(register int i=2;i<N;i++) {
if(!vis[i]) {
p[++p[0]]=i;
f[i]=power(i,k)-1;
}
for(register int j=1;j<=p[0]&&i*p[j]<N;j++) {
vis[i*p[j]]=true;
f[i*p[j]]=(int64)f[i]*(f[p[j]]+!(i%p[j]))%mod;
if(i%p[j]==0) break;
}
}
}
int main() {
m=getint(),k=getint();
sieve();
for(register int i=0;i<m;i++) {
printf("%d
",f[getint()]);
}
return 0;
}