====== 2020牛客暑期多校训练营(第四场) ====== ===== 比赛情况 ===== ^题号 ^A ^B ^C ^D ^E ^F ^G ^H ^I ^J ^K ^L ^M | ^状态 |- |O |O |- |- |O |- |O |Ø |- |- |- |- | //O 在比赛中通过 Ø 赛后通过 ! 尝试了但是失败了 - 没有尝试// **比赛时间** 2020-07-20 12:00-17:00 **提交记录** ===== 题解 ===== ==== B - Basic Gcd Problem ==== 题意:当$x>1$时,$f_c(x)=max_{i=1…x-1}c·f_c(gcd(i,x))$,当$x=1$时,$f_c(x)=1$,给出若干$n,c$,求$f_c(n)$ 题解:n的质因数的数目为$cnt_n$,可以分析出来问题的答案是$c^{cnt_n}$,硬分解可能会T,写个递推求质因数数目就行。 #include using namespace std; #define maxn 1000005 using namespace std; typedef long long ll; const int mod = 1e9+7; bool isprime[maxn]; int prime[maxn]; int f[maxn]; int cnt=0; void PJ(){ memset(isprime,0,sizeof(isprime)); cnt=0; for(int i=2;i<=1000000;i++){ if(!isprime[i]){ prime[cnt++]=i; } for(int j=0;j>= 1; } return ans; } int main() { PJ(); for (int i = 1;i<= 1000000;i++) for (int j = 0;j \\ ==== C - Count New String ==== 可以发现所谓所有$f(f(S,x_1,y_1),x_2-x_1+1,y_2-x_1+1)$本质不同字符串的个数,就相当于$\sum_{i=1}^n f(S,i,n)$中本质不同字符串个数,然后发现根据这个字符串的性质,如果从后向前更新,那么每个位置最多只需要修改10次,所以在广义sam上乱搞一下就行。 #include #include #include #define setIO(s) freopen(s".in","r",stdin) #define maxn 2000005 #define N 12 #define ll long long using namespace std; char str[maxn]; int last=1,tot=1,n,m; int ch[maxn][N],f[maxn][7],dis[maxn],rk[maxn]; int lst[maxn]; long long C[maxn],ans; void ins(int c,int id){ int np=++tot,p=last; last=np; if(ch[p][c]){ int q=ch[p][c]; if(dis[q]==dis[p]+1) last=q; else { int nq=++tot; last=nq; f[nq][id]=f[q][id],dis[nq]=dis[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); f[q][id]=nq; while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p][id]; } } else{ dis[np]=dis[p]+1; while(p&&!ch[p][c]) { ch[p][c]=np,p=f[p][id]; } if(!p) f[np][id]=1; else{ int q=ch[p][c],nq; if(dis[q]==dis[p]+1) f[np][id]=q; else{ nq=++tot; dis[nq]=dis[p]+1; memcpy(ch[nq],ch[q],sizeof(ch[q])); f[nq][id]=f[q][id],f[q][id]=f[np][id]=nq; while(p&&ch[p][c]==q) ch[p][c]=nq,p=f[p][id]; } } ans+=(dis[np]-dis[f[np][id]]); } } int main(){ //setIO("input"); //int t; scanf("%d",&t); //while(t--) //{ scanf("%s",str+1),n=strlen(str+1); lst[n+1] = 1; for (int i = n;i>=1;i--) { int j = i+1; while (str[i]>str[j] && j <= n) { str[j] = str[i]; j++; } last = lst[j]; while (j > i) { j--; ins(str[j]-'a',0); lst[j] = last; } } printf("%lld\n",ans); last = 1; //} return 0; } \\ ==== F - Finding the Order ==== 已知直线 $AB$ 平行于直线 $CD$,且已知 $|AC|,|AD|,|BC|,|BD|$ 四个值,问 $\overrightarrow{AB}$ 与 $\overrightarrow{CD}$ 同方向,还是与 $\overrightarrow{DC}$ 同方向。 分类讨论,如果 $|AC| > |BC|$ 说明点 $C$ 在靠近点 $B$ ,对点 $D$ 的讨论同样。 #include #define ALL(x) (x).begin(),(x).end() #define ll long long #define ull unsigned long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "<bc){ cout<<"AB//DC"<bc){ cout<<"AB//DC"< \\ ==== I - Investigating Legions ==== 个人理解,相当于给一个含有多个近似团的图,要还原出这些团。 大概就是乱搞。我的做法是先贪心取最小的没有确定的点,把和他相邻的点加到一个集合 $V$ 里,遍历所有其他没有确定的点,如果这个点和集合 $V$ 的连接度大于某个和 $S$ 有关的阈值,就把这个点加入当前正在确定的团里。最后仍有些点没有被确定,这时遍历所有确定的团,看他与哪个团的连接度最高就放进哪里。 #include #define ALL(x) (x).begin(),(x).end() #define ll long long #define ull unsigned long long #define pii_ pair #define mp_ make_pair #define pb push_back #define fi first #define se second #define rep(i,a,b) for(int i=(a);i<=(b);i++) #define per(i,a,b) for(int i=(a);i>=(b);i--) #define show1(a) cout<<#a<<" = "< \\ ===== 比赛总结与反思 =====