##比赛感想## 原本21:05开始的比赛,结果记成21:30了。。。晚了25分钟才开始[捂脸] 此次是Educational Round,因此还比较简单。node
前两道题一眼看去模拟+贪心,怕错仔细看了好几遍题,很快切掉 第三题,dfs+贪心 一开始想得有点简单,少了几种状况,写代码时才发现问题…… 悲伤地发现 写+调 这道题用了我很长时间…(这叫什么?基础不牢,地动山摇!)ios
而后,竟然只剩40分钟了……c++
第四题,啊啊啊! 图论,个人痛! 果断跳过 第五题,额,不就是个线段树么? n<=10 $^9$ ?很差很差,要动态开节点 噼里啪啦噼里啪啦…… 提交。为何超时了??? 个人常数真的这么大么?? 调一调…仍是不行。。(……比赛结束后才发现是编译器的问题……)git
而后,竟然只剩10分钟了……数组
第六题,啊啊啊!怎么又是图论! 图论,个人痛!果断跳过 第七题,数论,有关gcd 奇迹般地有了思路,惋惜,时间不够没写完……浏览器
接着,悲伤地发现比赛结束了。悲伤地发现我只作对了3道水题… 嗯,水平还有很大提高空间啊,前方路还很长……函数
##题目+题解##spa
###Codeforces 915###code
###A. Garden###排序
Luba要给花园浇水,花园长度为k 她有n个水桶,每一个水桶一次浇的长度为$a_i$ (不能多也不能少) 她要选择1个水桶,使她浇得最快,且不会有地方被浇两次,不会浇到花园外面 求她浇完的最短期。 (n,k,$a_i$ $\leq$ 100)
####想法#### 在ai中找到可整除k的最大的数,用k除以这个数即是答案
####代码####
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int main() { int n,k,i,x,ans=1000; scanf("%d%d",&n,&k); for(i=0;i<n;i++){ scanf("%d",&x); if(k%x!=0) continue; ans=min(ans,k/x); } printf("%d\n",ans); return 0; }
###B. Browser###
Luba在浏览器中打开了n个标签,从左到右标号1~n 她只需[l,r]的标签,因此她要把其余的标签关上 她的鼠标停在第pos个标签页上 设她某时间鼠标位置为i,她能够有两种操做: 1.关闭[1,i-1]或[i+1,n]中全部开着的标签 2.将鼠标移到第i-1或第i+1个标签上 (前提:移到的那个标签必须是开着的) 求她把除[l,r]外其余标签都关闭的最少操做数。 (n $\leq$ 100)
####想法#### 分状况考虑+贪心
####代码####
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; int main() { int n,pos,l,r,s; scanf("%d%d%d%d",&n,&pos,&l,&r); if(l==1 && r==n) printf("0\n"); else if(l==1) printf("%d\n",abs(r-pos)+1); else if(r==n) printf("%d\n",abs(l-pos)+1); else { s=min(abs(l-pos),abs(r-pos)); s+=r-l+2; printf("%d\n",s); } return 0; }
###C. Permute Digits###
给定a与b,求 将组成a的数字从新排列,组成的不大于b的最大整数 注意,输出的整数与a的位数要一致,前导0要输出 (a,b $\leq$ $10^{18}$ )
####想法#### 若b的位数比a大,那么直接贪心,将组成a的数字从大到小输出 不然,先把在b前补0使它与a的位数一致,接着从高位往低位考虑贪心,dfs回溯判断是否可行,若贪心到某一位发现 a的这一位<b的这一位,后面的位直接贪心
####代码####
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; int a[10]; char b[20],s[20]; int m,ans[20]; void Max(int cur){ for(int i=9;i>=0;i--) for(int j=0;j<a[i];j++) ans[cur++]=i; } bool dfs(int cur){ if(cur==m) return true; int i; if(a[i=b[cur]-'0']){ ans[cur]=i; a[i]--; if(dfs(cur+1)) return true; a[i]++; } for(i=i-1;i>=0;i--) if(a[i]){ ans[cur]=i; a[i]--; Max(cur+1); return true; } return false; } int main() { int i,n; scanf("%s",s); n=strlen(s); for(i=0;i<n;i++) a[s[i]-'0']++; scanf("%s",b); m=strlen(b); if(m>n) Max(0); else{ for(i=m-1;i>=0;i--) b[i+n-m]=b[i]; for(i=0;i<n-m;i++) b[i]='0'; m=n; dfs(0); } for(i=0;i<n;i++) printf("%d",ans[i]); printf("\n"); return 0; }
###D. Almost Acyclic Graph###
给定一个n个点m条边的有向图 问是否能够去掉一条边是图中再也不有环 ( 2 $\leq$ n $\leq$ 500 , 1 $\leq$ m $\leq$ min(n(n-1),100000) )
####想法#### tarjan找scc,过程当中记录某一个环 若scc数目==n,则yes 不然,对于记录下来的那个环,试着把每条边删一遍,跑tarjan 若删掉某条边后的scc数目==n,则yes 不然no (由于若删一条边知足条件的话,这条边必定在每一个环中都出现)
####代码####
#include<cstdio> #include<iostream> #include<algorithm> #include<cstring> using namespace std; const int N = 505; int dfn[N],map[N][N],low[N],vis[N],fa[N]; int scc,n,cnt; int s[N],t; int cir[N],tot; void dfs(int u){ dfn[u]=low[u]=++cnt; s[t++]=u; vis[u]=1; for(int v=1;v<=n;v++) if(map[u][v]){ if(!dfn[v]){ fa[v]=u; dfs(v); low[u]=min(low[u],low[v]); } else if(vis[v]){ low[u]=min(low[u],dfn[v]); if(!tot){ for(int i=u;i!=v;i=fa[i]) cir[tot++]=i; cir[tot++]=v; } } } if(dfn[u]==low[u]){ scc++; while(s[t-1]!=u) vis[s[--t]]=0; vis[s[--t]]=0; } } void tarjan() { for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i); } int main() { int i,m,x,y,flag; scanf("%d%d",&n,&m); for(i=0;i<m;i++) scanf("%d%d",&x,&y),map[x][y]=1; tarjan(); if(scc==n) puts("YES"); else{ flag=0; y=cir[tot-1]; for(i=0;i<tot;i++){ x=cir[i]; memset(dfn,0,sizeof(dfn)); map[x][y]=0; cnt=scc=0; tarjan(); if(scc==n) { flag=1; break; } map[x][y]=1; y=cir[i]; } if(flag) puts("YES"); else puts("NO"); } return 0; }
###E. Physical Education Lessons###
距学期结束还有n天,Alex须要在这n天上课 但因为他的学校上课时间常变更(共q次),因此他想知道每次变更后他须要上多少天课 有两种变更: 1.[l,r]都不上课 2.[l,r]都要上课 假设一开始他要上n天课 ( 1 $\leq$ n $\leq$ $10^9$ , 1 $\leq$ q $\leq$ 300000 )
####想法#### 标准的线段树啊 因为n最大1e9因此须要动态开节点
####代码####
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int N = 300005; int n; struct node{ node *ch[2]; int sum,lazy; }pool[N*50],*root; int cnt; void pushdown(node *p,int l,int r){ int mid=(l+r)>>1; if(!p->ch[0]){ p->ch[0]=&pool[++cnt]; p->ch[0]->sum=(mid-l+1); p->ch[0]->lazy=-1; } if(!p->ch[1]){ p->ch[1]=&pool[++cnt]; p->ch[1]->sum=(r-mid); p->ch[1]->lazy=-1; } if(p->lazy!=-1){ p->ch[0]->sum=(mid-l+1)*p->lazy; p->ch[1]->sum=(r-mid)*p->lazy; p->ch[0]->lazy=p->ch[1]->lazy=p->lazy; p->lazy=-1; } } void update(node *p) { p->sum=p->ch[0]->sum+p->ch[1]->sum; } void change(node *p,int l,int r,int L,int R,int k){ if(p->sum==(r-l+1)*k) return; if(l==L && r==R){ p->sum=k*(r-l+1); p->lazy=k; return; } pushdown(p,l,r); int mid=(l+r)>>1; if(mid>=R) change(p->ch[0],l,mid,L,R,k); else if(mid<L) change(p->ch[1],mid+1,r,L,R,k); else{ change(p->ch[0],l,mid,L,mid,k); change(p->ch[1],mid+1,r,mid+1,R,k); } update(p); } int main() { int q,i,k,l,r; scanf("%d%d",&n,&q); root=&pool[++cnt]; root->lazy=-1; root->sum=n; for(i=0;i<q;i++){ scanf("%d%d%d",&l,&r,&k); change(root,1,n,l,r,k-1); printf("%d\n",root->sum); } return 0; }
###F. Imbalance Value of a Tree###
给定一棵n个节点的树,每一个点都有权值ai 函数I(x,y)的值为从x到y的惟一路径上点权最大-点权最小 (包括路径上的点x,y) 求全部点对的I(x,y)之和 (n $\leq$ $10^6$)
####想法#### 如果暴力枚举每对点的话,就算求I为O(1)也会超时 因而考虑函数I,其实是求每对点间路径上的点权最小值之和 及 最大值之和 先考虑最小值 对于点权最小的那个点,显然全部通过它的路径上点权最小的都是它 对于点权次小的点,全部通过它且不通过点权最小点的路径上,点权最小的都为它 …… 对于点权次大的点,它被计算当且仅当有一条边链接它与点权最大的点 对于点权最大的点,它不会被计算
因为是树,两两点间路径是惟一的 因而就有这样一种作法:将每条边按照所连两点权值较小值 从大到小排序 借助并查集,按排好的顺序合并边所连的两点,共n-1次 每次合并,两个集合中各任取一点,它们之间的路径上点权最小值为 这条边所连两点权值较小值
最大值同理,只不过是按边所连两点权值较大值 从小到大排序
####代码####
#include<cstdio> #include<iostream> #include<algorithm> using namespace std; const int N = 1000005; typedef long long ll; struct edge{ int u,v; }e[N]; int val[N],fa[N],size[N]; bool cmp1(edge x,edge y) { return min(val[x.u],val[x.v])>min(val[y.u],val[y.v]); } bool cmp2(edge x,edge y) { return max(val[x.u],val[x.v])<max(val[y.u],val[y.v]); } int Getfa(int x) { return fa[x]==x ? x : fa[x]=Getfa(fa[x]); } int n; int main() { int i,x,y,v; ll ans=0; scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",&val[i]); for(i=1;i<n;i++) scanf("%d%d",&e[i].u,&e[i].v); for(i=1;i<=n;i++) fa[i]=i,size[i]=1; sort(e+1,e+n,cmp1); for(i=1;i<n;i++){ v=min(val[e[i].u],val[e[i].v]); x=Getfa(e[i].u); y=Getfa(e[i].v); if(size[x]<size[y]) swap(x,y); ans-=(ll)v*size[x]*size[y]; fa[y]=x; size[x]+=size[y]; } for(i=1;i<=n;i++) fa[i]=i,size[i]=1; sort(e+1,e+n,cmp2); for(i=1;i<n;i++){ v=max(val[e[i].u],val[e[i].v]); x=Getfa(e[i].u); y=Getfa(e[i].v); if(size[x]<size[y]) swap(x,y); ans+=(ll)v*size[x]*size[y]; fa[y]=x; size[x]+=size[y]; } printf("%lld\n",ans); return 0; }
###G. Coprime Arrays###
人们管知足 gcd($a_1$,$a_2$,…,$a_n$) =1的数组a叫Coprime Array 给出n,k 设对于i $\in$ [1,k] 知足每一个元素都$\in$[1,i] 的Coprime Array的个数为$b_i$ 求$\sum\limits_{i=1}^k$ ( $b_i$ ^ i) (n,k $\leq$ 2 $\times$ $10^6$)
####想法一####
定义对于一个数组a,lgcd=gcd($a_1$,$a_2$,…,$a_n$)
设 每一个元素都$\in$[1,i]的 知足lgcd=j 的数组个数为w[i][j] 那么b[i]=$i^n$-$\sum\limits_{j=2}^i$ w[i][j] 很容易发现,w[i][j]=b[$ \frac {i} {j}$ ] 接下来跟“余数求和”有那么一点像 愉快地超时了……
####代码####
#include<cstdio> #include<iostream> #include<algorithm> #define P 1000000007 using namespace std; typedef long long ll; const int N = 2000005; int PowerMod(int x,int b){ int ret=1; while(b){ if(b&1) ret=((ll)ret*x)%P; x=((ll)x*x)%P; b>>=1; } return ret; } int f[N]; int n,k; int main() { int i,l,r,ans; scanf("%d%d",&n,&k); f[1]=1; ans=0; for(i=2;i<=k;i++){ f[i]=PowerMod(i,n); for(l=2,r;l<=i;l=r+1){ r=i/(i/l); if(r>i) r=i; f[i]=(f[i]-((ll)r-l+1)*(f[i/l]-P))%P; } ans=(ans+(f[i]^i))%P; } printf("%d\n",ans); return 0; }
####想法二#### 想想能够发现,b数组是递增的 设f[i]=b[i]-b[i-1]
f[i]统计的是至少有一个元素为i的Coprime Array个数 和想法一的思路有一点点像 设 每一个元素都$\in$[1,i]的 知足lgcd=j 且至少有一个元素为i 的数组个数为w[i][j] 因为肯定数组中必定有一个数是i,那么算出的lgcd值只能为i的约数 很容易发现,w[i][j]=f[$\frac {i} {j} $ ] 那么f[i]=$i^n$ - $(i-1)^n$ - $\sum${w[i][j] | j $\in$ [1,i] , i mod j=0 } (无比神奇的sum用法。。。)
####代码####
#include<cstdio> #include<iostream> #include<algorithm> #define P 1000000007 using namespace std; typedef long long ll; const int N = 2000005; ll PowerMod(int x,int b){ ll ret=1; while(b){ if(b&1) ret=(ret*x)%P; x=((ll)x*x)%P; b>>=1; } return ret; } ll f[N],p[N]; int n,k; int main() { int i,j; ll ans,sum; scanf("%d%d",&n,&k); ans=sum=0; for(i=1;i<=k;i++){ p[i]=PowerMod(i,n); f[i]=(f[i]+p[i]-p[i-1]+P)%P; sum=(sum+f[i])%P; ans=(ans+(sum^i))%P; for(j=i*2;j<=k;j+=i) f[j]=(f[j]-f[i]+P)%P; } printf("%d\n",ans); return 0; }
终于……