这里会显示出您选择的修订版和当前版本之间的差别。
| 后一修订版 | 前一修订版 | ||
|
2020-2021:teams:die_java:front_page_springtraining5 [2020/05/15 22:22] fyhssgss 创建 |
2020-2021:teams:die_java:front_page_springtraining5 [2020/05/23 12:10] (当前版本) fyhssgss [训练详情] |
||
|---|---|---|---|
| 行 3: | 行 3: | ||
| [[https://vjudge.net/contest/373163|比赛网址]] | [[https://vjudge.net/contest/373163|比赛网址]] | ||
| - | ===== 训练结果 ===== | + | ===== 训练详情 ===== |
| - | + | * 时间:2020-5-10 13:00~18:00 | |
| - | * rank:算了这个别说了 | + | * rank: |
| - | * 完成情况:3/6/13 | + | * 完成情况:''%%3/6/13%%'' |
| ===== 题解 ===== | ===== 题解 ===== | ||
| 行 13: | 行 13: | ||
| 补题 | 补题 | ||
| - | ==== 题意 ==== | + | === 题意 === |
| 给了一个序列,要求实现两种操作 | 给了一个序列,要求实现两种操作 | ||
| 行 22: | 行 22: | ||
| === 题解 === | === 题解 === | ||
| - | 开始想到线段树套线性基,发现时间和空间都爆了。后发现我们可以记录 $a[1...i]$ 的线性基,添加时候则从高位到低位,尽量用当前的基去替换之前的基,这样能使所有的基离r更近。查询的时候只用位置大于 $l$ 的基。 | + | 开始想到线段树套线性基,发现时间和空间都爆了。后发现我们可以记录 $a[1... i]$ 的线性基,添加时候则从高位到低位,尽量用当前的基去替换之前的基,这样能使所有的基离r更近。查询的时候只用位置大于 $l$ 的基。 |
| + | \\ | ||
| + | <hidden B> | ||
| <code cpp> | <code cpp> | ||
| #include<iostream> | #include<iostream> | ||
| 行 109: | 行 111: | ||
| } | } | ||
| </code> | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| ===== D Vacation ===== | ===== D Vacation ===== | ||
| + | solved by wxg | ||
| + | |||
| + | === 题意 === | ||
| + | |||
| + | 有 $n$ 辆汽车在过绿灯,每辆车给了距离红绿灯的位置,长度和最大速度。不能超车。假设每辆车都按最优方案驾驶,问最后一辆车过红绿灯的时间 | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | 可以想象,最后几辆车一定是贴在一起通过的,我们只需要枚举 $x$ ,计算最后 $x$ 俩车贴贴后通过时间,取一个最大值即可。 | ||
| + | \\ | ||
| + | <hidden D> | ||
| + | <code cpp> | ||
| + | #include<iostream> | ||
| + | #include<cstdio> | ||
| + | #include<cstring> | ||
| + | #include<algorithm> | ||
| + | using namespace std; | ||
| + | int read() | ||
| + | { | ||
| + | int k=0,f=1;char c=getchar(); | ||
| + | for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; | ||
| + | for(;isdigit(c);c=getchar()) k=k*10+c-'0';return f*k; | ||
| + | } | ||
| + | const int N=100055; | ||
| + | int n,m,l[N],s[N],v[N]; | ||
| + | int main() | ||
| + | { | ||
| + | while(scanf("%d",&n)!=EOF) | ||
| + | { | ||
| + | for(int i=0;i<=n;i++) | ||
| + | scanf("%d",&l[i]); | ||
| + | for(int i=0;i<=n;i++) | ||
| + | scanf("%d",&s[i]); | ||
| + | for(int i=0;i<=n;i++) | ||
| + | scanf("%d",&v[i]); | ||
| + | double ans=1.*s[0]/v[0],sum=0; | ||
| + | for(int i=1;i<=n;i++) | ||
| + | { | ||
| + | sum+=l[i]; | ||
| + | ans=max(ans,((sum+s[i])/v[i])); | ||
| + | } | ||
| + | printf("%.8f\n",ans); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| + | ===== E Path ===== | ||
| + | |||
| + | 口胡 by fyh ,written by wxg | ||
| + | |||
| + | === 题意 === | ||
| + | |||
| + | 给了一个图,去掉一个边的代价为边权,问为使 $1-n$ 的最短路变长,最小代价是多少。 | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | 求出最短路径的新图,可以看出答案为最小割 | ||
| + | \\ | ||
| + | <hidden E> | ||
| + | <code cpp> | ||
| + | #include<iostream> | ||
| + | #include<cstdio> | ||
| + | #include<algorithm> | ||
| + | #include<cstring> | ||
| + | #include<queue> | ||
| + | #define ll long long | ||
| + | #define int long long | ||
| + | using namespace std; | ||
| + | int read() | ||
| + | { | ||
| + | int k=0,f=1;char c=getchar(); | ||
| + | for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; | ||
| + | for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f; | ||
| + | } | ||
| + | const int N=200055; | ||
| + | const ll inf=1ll<<60; | ||
| + | typedef pair<int,int> P; | ||
| + | int T,n,m,s,t; | ||
| + | int tot=1,iter[N],head[N],to[N],nextt[N],w[N],d[N],vis[N]; | ||
| + | queue<int> q; | ||
| + | struct graph | ||
| + | { | ||
| + | int tot=0,to[N],nextt[N],head[N],w[N]; | ||
| + | int u[N],v[N],c[N]; | ||
| + | ll dis[N]; | ||
| + | priority_queue <P,vector<P>,greater<P> > q; | ||
| + | void add(int a,int b,int c) | ||
| + | { | ||
| + | to[++tot]=b; | ||
| + | w[tot]=c; | ||
| + | nextt[tot]=head[a]; | ||
| + | head[a]=tot; | ||
| + | } | ||
| + | void dij() | ||
| + | { | ||
| + | for(int i=2;i<=n;i++) | ||
| + | dis[i]=1ll<<60; | ||
| + | dis[1]=0; | ||
| + | q.push(P(0,1)); | ||
| + | while(!q.empty()) | ||
| + | { | ||
| + | P k=q.top();q.pop(); | ||
| + | int u=k.second;if(dis[u]<k.first) continue; | ||
| + | for(int i=head[u];i;i=nextt[i]) | ||
| + | if(dis[to[i]]>dis[u]+w[i]) | ||
| + | { | ||
| + | dis[to[i]]=dis[u]+w[i]; | ||
| + | q.push(P(dis[to[i]],to[i])); | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | }G; | ||
| + | void add(int a,int b,int c) | ||
| + | { | ||
| + | to[++tot]=b; | ||
| + | w[tot]=c; | ||
| + | nextt[tot]=head[a]; | ||
| + | head[a]=tot; | ||
| + | } | ||
| + | bool bfs() | ||
| + | { | ||
| + | for(int i=1;i<=n;i++) d[i]=-1; | ||
| + | d[s]=0;q.push(s); | ||
| + | while(!q.empty()) | ||
| + | { | ||
| + | int u=q.front();q.pop(); | ||
| + | for(int i=head[u];i;i=nextt[i]) | ||
| + | if(w[i]>0&&d[to[i]]==-1) | ||
| + | d[to[i]]=d[u]+1,q.push(to[i]); | ||
| + | } | ||
| + | return d[t]!=-1; | ||
| + | } | ||
| + | int dfs(int u,int f) | ||
| + | { | ||
| + | if(f==0||u==t) return f; | ||
| + | for(int &i=iter[u];i;i=nextt[i]) | ||
| + | { | ||
| + | if(d[to[i]]==d[u]+1&&w[i]>0) | ||
| + | { | ||
| + | int ff=dfs(to[i],min(f,w[i])); | ||
| + | if(ff>0) | ||
| + | { | ||
| + | w[i]-=ff;w[i^1]+=ff; | ||
| + | return ff; | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | int dinic() | ||
| + | { | ||
| + | int fl=0,f; | ||
| + | while(1) | ||
| + | { | ||
| + | if(!bfs()) return fl; | ||
| + | for(int i=1;i<=n;i++) | ||
| + | iter[i]=head[i]; | ||
| + | while(f=dfs(s,inf)) fl+=f; | ||
| + | } | ||
| + | return fl; | ||
| + | } | ||
| + | main() | ||
| + | { | ||
| + | for(T=read();T;T--) | ||
| + | { | ||
| + | G.tot=0; | ||
| + | for(int i=1;i<=n;i++) | ||
| + | G.head[i]=0; | ||
| + | for(int i=1;i<=tot;i++) | ||
| + | head[i]=0; | ||
| + | tot=1; | ||
| + | n=read();m=read(); | ||
| + | for(int i=1;i<=m;i++) | ||
| + | { | ||
| + | int a,b,c; | ||
| + | a=read();b=read();c=read(); | ||
| + | G.u[i]=a;G.v[i]=b;G.c[i]=c; | ||
| + | G.add(a,b,c); | ||
| + | } | ||
| + | G.dij(); | ||
| + | for(int i=1;i<=m;i++) | ||
| + | { | ||
| + | if(G.dis[G.u[i]]+G.c[i]==G.dis[G.v[i]]) | ||
| + | { | ||
| + | add(G.u[i],G.v[i],G.c[i]); | ||
| + | add(G.v[i],G.u[i],0); | ||
| + | } | ||
| + | } | ||
| + | s=1;t=n; | ||
| + | printf("%lld\n",dinic()); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| + | ===== F Typewriter ===== | ||
| + | |||
| + | solved by wxg | ||
| + | === 题意 === | ||
| + | |||
| + | 给了一个字符串,你现在要花最小代价构造出该字符串,有两种构造方法 | ||
| + | |||
| + | - 花 $p$ 代价在当前字符串后添加一个字符。 | ||
| + | - 花 $q$ 代价在当前字符串后添加一个当前字符串的字串 | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | dp ,$dp[i]$ 表示构造出长度 $i$ 的字符串的最小代价,有两种转移 | ||
| + | |||
| + | - $dp[i]=dp[i-1]+p$ | ||
| + | - $dp[i]=dp[j]+q$ , $j$ 为满足 $s[1... j]$ 中存在 $s[j+1... i]$ 的子串 | ||
| + | |||
| + | 问题是 $j$ 怎么求? | ||
| + | |||
| + | 首先随着 $i$ 增加,$j$ 是单调的 | ||
| + | |||
| + | 我们可以构建 $s[1... j]$ 的后缀自动机,当 dp 到 $i$ 时,我们在后缀自动机上匹配 $s[i]$ ,若匹配长度与 $j$ 的和大于等于 $i$ ,说明 $j$ 不用增加,反之则增加 $j$ 直到大于等于为止。 | ||
| + | \\ | ||
| + | <hidden f> | ||
| + | <code cpp> | ||
| + | #include<iostream> | ||
| + | #include<cstdio> | ||
| + | #include<algorithm> | ||
| + | #include<cstring> | ||
| + | #define ll long long | ||
| + | using namespace std; | ||
| + | int read() | ||
| + | { | ||
| + | int k=0,f=1;char c=getchar(); | ||
| + | for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; | ||
| + | for(;isdigit(c);c=getchar()) k=k*10+c-'0';return k*f; | ||
| + | } | ||
| + | const int N=400055; | ||
| + | int n,m,ch[N][26],mxlen,P,Q; | ||
| + | ll dp[N]; | ||
| + | int last,tot,link[N],len[N],size[N]; | ||
| + | char s[N]; | ||
| + | void init() | ||
| + | { | ||
| + | for(int i=0;i<=tot;i++) memset(ch[i],0,sizeof(ch[i])); | ||
| + | last=tot=0;len[0]=0;link[0]=-1; | ||
| + | } | ||
| + | void extend(int c) | ||
| + | { | ||
| + | int cur=++tot,p=last;len[cur]=len[last]+1;size[cur]=1; | ||
| + | for(;p!=-1&&!ch[p][c];p=link[p]) ch[p][c]=cur; | ||
| + | if(p==-1) link[cur]=0; | ||
| + | else | ||
| + | { | ||
| + | int q=ch[p][c]; | ||
| + | if(len[p]+1==len[q]) link[cur]=q; | ||
| + | else | ||
| + | { | ||
| + | int clone=++tot; | ||
| + | len[clone]=len[p]+1; | ||
| + | link[clone]=link[q]; | ||
| + | memcpy(ch[clone],ch[q],sizeof(ch[q])); | ||
| + | for(;p!=-1&&ch[p][c]==q;p=link[p]) | ||
| + | ch[p][c]=clone; | ||
| + | link[q]=clone;link[cur]=clone; | ||
| + | } | ||
| + | } | ||
| + | last=cur; | ||
| + | } | ||
| + | int get(int p,int c,int opt) | ||
| + | { | ||
| + | if(ch[p][c]) | ||
| + | { | ||
| + | p=ch[p][c]; | ||
| + | if(opt==1) mxlen++; | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | while(p!=-1&&!ch[p][c]) | ||
| + | p=link[p]; | ||
| + | if(p==-1) p=0,mxlen=0; | ||
| + | else mxlen=len[p]+1,p=ch[p][c]; | ||
| + | } | ||
| + | if(opt) return mxlen; | ||
| + | return p; | ||
| + | } | ||
| + | int main() | ||
| + | { | ||
| + | while(scanf("%s",s+1)!=EOF) | ||
| + | { | ||
| + | int l=strlen(s+1); | ||
| + | init();scanf("%d%d",&P,&Q); | ||
| + | int inslen=0,pos=0;mxlen=0; | ||
| + | for(int i=1;i<=l;i++) | ||
| + | { | ||
| + | dp[i]=dp[i-1]+P; | ||
| + | if(get(pos,s[i]-'a',1)+inslen>=i) | ||
| + | { | ||
| + | dp[i]=min(dp[i],dp[inslen]+Q); | ||
| + | pos=get(pos,s[i]-'a',0); | ||
| + | } | ||
| + | else | ||
| + | { | ||
| + | while(get(pos,s[i]-'a',1)+inslen<i) | ||
| + | { | ||
| + | extend(s[++inslen]-'a'); | ||
| + | } | ||
| + | pos=get(pos,s[i]-'a',0); | ||
| + | if(inslen<i) | ||
| + | dp[i]=min(dp[i],dp[inslen]+Q); | ||
| + | } | ||
| + | // cout<<inslen<<" "<<mxlen<<endl; | ||
| + | } | ||
| + | printf("%lld\n", dp[l]); | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| + | ===== I String ===== | ||
| + | |||
| + | 补题 by hxm | ||
| + | |||
| + | === 题意 === | ||
| + | |||
| + | 给定一个只包含小写字母的字符串,要求选出一个长度为$K$的子序列,满足字典序最小,同时子序列中每个字母的出现次数都在一个各自给定的范围$[L,R]$内 | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | 贪心。 当前位置能选字典序小的就选字典序小的。 对于子序的第$i$个位置,从$a$开始枚举到$z$尝试将当前位置后第一个该字符放入,然后看看后面剩下的子串能不能至少满足能构成长度为$K$的合法子串。具体查看如下: 1、剩下能用字符能否至少构成长度为$K$ 2、剩下每个字符数量能否至少达到下界$L_i$,这个下界即要查看每个字符的剩余字符数是否足够,还要查看子序列剩余字符数能否提供所有的字符达到下界。 | ||
| + | |||
| + | 然后就完了。【很不明白比赛是怎么没调出来,,】 | ||
| + | \\ | ||
| + | <hidden I> | ||
| + | <code cpp> | ||
| + | #include<algorithm> | ||
| + | #include<iostream> | ||
| + | #include<cstdlib> | ||
| + | #include<cstring> | ||
| + | #include<cstdio> | ||
| + | #include<vector> | ||
| + | #include<queue> | ||
| + | #include<cmath> | ||
| + | #include<map> | ||
| + | #include<set> | ||
| + | #define LL long long int | ||
| + | #define REP(i,n) for (int i = 1; i <= (n); i++) | ||
| + | #define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt) | ||
| + | #define cls(s,v) memset(s,v,sizeof(s)) | ||
| + | #define mp(a,b) make_pair<int,int>(a,b) | ||
| + | #define cp pair<int,int> | ||
| + | using namespace std; | ||
| + | const int maxn = 100005,maxm = 100005,INF = 0x3f3f3f3f; | ||
| + | inline int read(){ | ||
| + | int out = 0,flag = 1; char c = getchar(); | ||
| + | while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();} | ||
| + | while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();} | ||
| + | return flag ? out : -out; | ||
| + | } | ||
| + | vector<int> pos[26]; | ||
| + | int pi[26],K,n; | ||
| + | int sum[maxn][26]; | ||
| + | int cnt[maxn],L[maxn],R[maxn]; | ||
| + | char s[maxn],ans[maxn]; | ||
| + | void init(){ | ||
| + | for (int i = 0; i < 26; i++){ | ||
| + | cnt[i] = 0; pos[i].clear(); pi[i] = 0; | ||
| + | } | ||
| + | } | ||
| + | int main(){ | ||
| + | while (~scanf("%s",s + 1)){ | ||
| + | init(); | ||
| + | K = read(); n = strlen(s + 1); | ||
| + | for (int i = 0; i < 26; i++) L[i] = read(),R[i] = read(); | ||
| + | for (int i = 1; i <= n; i++){ | ||
| + | int u = s[i] - 'a'; | ||
| + | pos[u].push_back(i); | ||
| + | for (int j = 0; j < 26; j++) sum[i][j] = sum[i - 1][j]; | ||
| + | sum[i][u]++; | ||
| + | } | ||
| + | //cout << sum[2][0] << endl; | ||
| + | int u = 1,tag = true; | ||
| + | for (int i = 1; i <= K; i++){ | ||
| + | tag = false; | ||
| + | for (int j = 0; j < 26; j++){ | ||
| + | //printf("[%d,%d]\n",i,j); | ||
| + | if (cnt[j] >= R[j]) continue; | ||
| + | while (pi[j] < pos[j].size() && pos[j][pi[j]] < u) pi[j]++; | ||
| + | if (pi[j] >= pos[j].size()) continue; | ||
| + | int tmp = 0; | ||
| + | //puts("LXT"); | ||
| + | for (int k = 0; k < 26; k++) tmp += min(R[k] - cnt[k],sum[n][k] - sum[pos[j][pi[j]] - 1][k]); | ||
| + | if (tmp < K - i + 1) continue; | ||
| + | //puts("LXT"); | ||
| + | int flag = true; tmp = 0; | ||
| + | for (int k = 0; k < 26; k++){ | ||
| + | if (sum[n][k] - sum[pos[j][pi[j]] - 1][k] < L[k] - cnt[k]) flag = false; | ||
| + | if (k != j){ | ||
| + | if (L[k] > cnt[k]) tmp += L[k] - cnt[k]; | ||
| + | } | ||
| + | } | ||
| + | if (tmp > K - i) continue; | ||
| + | //puts("LXT"); | ||
| + | if (!flag) continue; | ||
| + | cnt[j]++; | ||
| + | ans[i] = j + 'a'; | ||
| + | u = pos[j][pi[j]++] + 1; | ||
| + | tag = true; | ||
| + | //puts("pick"); | ||
| + | break; | ||
| + | } | ||
| + | if (!tag) {puts("-1"); break;} | ||
| + | } | ||
| + | if (tag){ | ||
| + | for (int i = 1; i <= K; i++) putchar(ans[i]); | ||
| + | puts(""); | ||
| + | } | ||
| + | } | ||
| + | return 0; | ||
| + | } | ||
| + | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| + | ===== K Function ===== | ||
| + | |||
| + | 补题 by fyh | ||
| + | === 题意 === | ||
| + | |||
| + | $$ | ||
| + | 求\sum_{i=1}^ngcd(\lfloor^3\sqrt{i}\rfloor,i),n\leq10^{21} | ||
| + | $$ | ||
| + | |||
| + | === 题解 === | ||
| + | |||
| + | 很自然的对立方数进行划分, $$ | ||
| + | \sum^n_{i=1}gcd(\lfloor^3\sqrt{i}\rfloor,i)=\sum_{i=1}^{\lfloor^3\sqrt{n}\rfloor}\sum_{j=(i-1)^3+1}^{i^3}gcd(i,j)+\sum^n_{i=\lfloor^3\sqrt{n}\rfloor^3}gcd(\lfloor^3\sqrt{n}\rfloor,i) | ||
| + | $$ 我们发现这个式子出现了很多次$\sum_{i=1}^{n}gcd(i,a)$, $$ | ||
| + | \sum_{i=1}^{n}gcd(i,a)=\sum_{x|a}x\sum^n_{i=1}[gcd(i,a)==x]=\sum_{x|a}x\sum_{i=1}^{\lfloor\frac{n}{x}\rfloor}[gcd(i,\frac{a}{x})==1] | ||
| + | $$ | ||
| + | |||
| + | $$ | ||
| + | =\sum_{x|a}x\sum_{i=1}^{\lfloor\frac{n}{x}\rfloor}\sum_{k|gcd(i,\frac{a}{x})}\mu(k)=\sum_{x|a}x\sum_{k|\frac{a}{x}}\mu(k)\sum_{i=1}^{\lfloor\frac{n}{x}\rfloor}[k|i]=\sum_{x|a}x\sum_{k|\frac{a}{x}}\mu(k)\lfloor\frac{n}{xk}\rfloor | ||
| + | $$ | ||
| + | |||
| + | 设$xk=d$,则上述式子等于 $$ | ||
| + | \sum_{d|a}\lfloor\frac{n}{d}\rfloor\sum_{x|d}x\mu(\frac{d}{x})=\sum_{d|a}\lfloor\frac{n}{d}\rfloor\phi(d) | ||
| + | $$ 故$\sum_{i=l}^{r}gcd(i,a)=\sum_{d|a}(\lfloor\frac{r}{d}\rfloor-\lfloor\frac{l-1}{d}\rfloor)\phi(d)$,对于原式右半部分的内容我们便可以通过数论分块在$O(^6\sqrt{n})$的时间内解决。 | ||
| + | |||
| + | 继续展开左边的式子: $$ | ||
| + | \sum_{i=1}^{\lfloor^3\sqrt{n}\rfloor}\sum_{j=(i-1)^3+1}^{i^3}gcd(i,j)=\sum_{i=1}^{\lfloor^3\sqrt{n}\rfloor}\sum_{d|i}(\lfloor\frac{(i+1)^3-1}{d}\rfloor-\lfloor\frac{i^3-1}{d}\rfloor)\phi(d) | ||
| + | $$ 设$xd=i$,可继续写成 $$ | ||
| + | \sum_{d=1}^{\lfloor^3\sqrt{n}\rfloor}\phi(d)\sum_{x=1}^{\lfloor\frac{\lfloor^3\sqrt{n}\rfloor-1}{d}\rfloor}(\lfloor\frac{(xd+1)^3-1}{d}\rfloor-\lfloor\frac{(xd)^3-1}{d}\rfloor)\phi(d) | ||
| + | $$ 左边那个整除余数是$0$,右边余数是$d-1$,再进一步展开得 $$ | ||
| + | \sum_{d=1}^{\lfloor^3\sqrt{n}\rfloor}\phi(d)\sum_{x=1}^{\lfloor\frac{\lfloor^3\sqrt{n}\rfloor-1}{d}}3dx^2+3x+1 | ||
| + | $$ 接下来就是平方和公式和等差数列求和,设$y=\lfloor\frac{\lfloor^3\sqrt{n}\rfloor-1}{d}\rfloor$,得: $$ | ||
| + | \sum_{d=1}^{\lfloor^3\sqrt{n}\rfloor}\phi(d)d\frac{y(y+1)(2y+1)}{2}+\sum_{d=1}^{\lfloor^3\sqrt{n}\rfloor}\phi(d)(\frac{y(y+1)}{2}+y) | ||
| + | $$ $y$也是一个整除形式,故依旧可以用数论分块维护,通过$O(^3\sqrt{n})$预处理出$\sum \phi(i)i$和$\sum \phi(i)$,就可以在$O(^{6}\sqrt{n})$的时间内处理每一组询问了。总时间复杂度$O(^3\sqrt{n}+^{6}\sqrt{n}*T)$ | ||
| + | |||
| + | 注意,读入要用%%__%%int128,但是在开数组的时候都要开int,否则会爆空间。 | ||
| + | \\ | ||
| + | <hidden K> | ||
| + | <code cpp> | ||
| + | #include<bits/stdc++.h> | ||
| + | using namespace std; | ||
| + | #define mem(a,b) memset(a,b,sizeof(a)) | ||
| + | typedef long long LL; | ||
| + | typedef pair<int,int> PII; | ||
| + | #define X first | ||
| + | #define Y second | ||
| + | inline __int128 read() | ||
| + | { | ||
| + | __int128 x=0,f=1;char c=getchar(); | ||
| + | while(!isdigit(c)){if(c=='-')f=-1;c=getchar();} | ||
| + | while(isdigit(c)){x=x*10+c-'0';c=getchar();} | ||
| + | return x*f; | ||
| + | } | ||
| + | __int128 n,ans; | ||
| + | const int MOD=998244353,maxN=10000000; | ||
| + | int T,prime[maxN+10],len,A,sqrt3N,phi[maxN+10],phii[maxN+10],pre[maxN+10],inv2=499122177; | ||
| + | bool vis[maxN+10]; | ||
| + | void calc() | ||
| + | { | ||
| + | phi[1]=1;phii[1]=1; | ||
| + | for(int i=2;i<=maxN;i++) | ||
| + | { | ||
| + | if(!vis[i])prime[++len]=i,phi[i]=i-1; | ||
| + | for(int j=1;j<=len;j++) | ||
| + | { | ||
| + | if(i*prime[j]>maxN)break; | ||
| + | vis[i*prime[j]]=1; | ||
| + | if(i%prime[j]==0){ | ||
| + | phi[i*prime[j]]=phi[i]*prime[j]; | ||
| + | break; | ||
| + | } | ||
| + | else phi[i*prime[j]]=phi[i]*(prime[j]-1); | ||
| + | } | ||
| + | } | ||
| + | for(int i=1;i<=maxN;i++)phii[i]=((LL)i*(LL)phi[i])%MOD; | ||
| + | for(int i=1;i<=maxN;i++)pre[i]=((LL)pre[i-1]+(LL)phi[i])%MOD,phii[i]=((LL)phii[i-1]+(LL)phii[i])%MOD; | ||
| + | } | ||
| + | __int128 sqrt3(__int128 N) | ||
| + | { | ||
| + | __int128 l=0,r=1e9; | ||
| + | while(r-l>1) | ||
| + | { | ||
| + | __int128 mid=(l+r)/2; | ||
| + | if(mid*mid*mid<N)l=mid; | ||
| + | else r=mid; | ||
| + | } | ||
| + | return (r*r*r<=N) ? r : l; | ||
| + | } | ||
| + | int main() | ||
| + | { | ||
| + | calc(); | ||
| + | scanf("%d",&T); | ||
| + | while(T--) | ||
| + | { | ||
| + | n=read(); | ||
| + | sqrt3N=(int)sqrt3(n); | ||
| + | __int128 ans=0; | ||
| + | for(int d=1;d*d<=sqrt3N;d++) | ||
| + | if(sqrt3N%d==0) | ||
| + | { | ||
| + | ans=(ans+(n/d-((__int128)sqrt3N*(__int128)sqrt3N*(__int128)sqrt3N-1)/d)%MOD*phi[d])%MOD; | ||
| + | if(d*d!=sqrt3N) | ||
| + | { | ||
| + | int t=sqrt3N/d; | ||
| + | ans=(ans+(n/t-((__int128)sqrt3N*(__int128)sqrt3N*(__int128)sqrt3N-1)/t)%MOD*phi[t])%MOD; | ||
| + | } | ||
| + | } | ||
| + | for(int l=1,r=0;l<=sqrt3N-1;l=r+1) | ||
| + | { | ||
| + | int x=(sqrt3N-1)/l; | ||
| + | r=min(sqrt3N-1,(sqrt3N-1)/((sqrt3N-1)/l)); | ||
| + | LL tmp1=(phii[r]-phii[l-1]+MOD)%MOD,tmp2=(pre[r]-pre[l-1]+MOD)%MOD; | ||
| + | ans=(ans+tmp1*(LL)inv2%MOD*x%MOD*(x+1)%MOD*(2*x+1))%MOD; | ||
| + | ans=((ans+tmp2*(x+1)%MOD*x%MOD*inv2%MOD*3%MOD)%MOD+x*tmp2%MOD)%MOD; | ||
| + | } | ||
| + | cout<<(LL)ans<<endl; | ||
| + | } | ||
| + | | ||
| + | return 0; | ||
| + | } | ||
| + | |||
| + | </code> | ||
| + | </hidden> | ||
| + | \\ | ||
| + | ===== 训练实况 ===== | ||
| + | |||
| + | 0~1h fyh摸鱼了15min,hxm读**E**,wxg秒A**D**,fyh写**E**,疯狂CE+RE,把数据量调大之后变成WA wxg&hxm思考**F** **B** | ||
| + | |||
| + | 1~2h fyh仍在尝试**E** hxm读**M** 与**I**,fyh放弃了**E**,wxg开始写**F** | ||
| + | |||
| + | 2~3h fyh读**K,L** ,与hxm交流**I**,澄清题意后口糊出**I**做法,wxgA**F**,hxm开始写**M**并写完,但是发现误解题意,后放弃,wxg打算重新写**E** | ||
| + | |||
| + | 3~4h wxgA**E** ,fyh推**K**无果,开始写**I** | ||
| + | |||
| + | 4~5h wxg,hxm讨论**B**,口糊出来,没有写,fyh&hxm调**I**无果 | ||
| + | |||
| + | 结果:wxg全场最佳 | ||
| + | |||
| + | ===== 训练总结 ===== | ||
| + | |||
| + | 总结&反思:模板的整理是个大问题 ,本次用的全部是高中时候的板子,fyh调板子题调了一年,问题出在哪最后也不知道(重写大法好)**I**没写出来是因为代码能力差+脑子持续掉线+细节没想清楚 | ||
| + | |||
| + | ===== 改进 ===== | ||
| + | |||
| + | * 一个题至少要两个读,之后再核对题目大意是否一致,否则白给 | ||
| + | * 板子的整理 | ||