来自FallDream的博客,未经允许,请勿转载,谢谢。
由于人傻又菜 所以这次又滚去div2了 一堆结论题真的可怕
看见E题不是很有思路 然后就去大力搞F题 T了最后一个点 真的绝望 但是还是上紫了
感觉每次cf都在乱打 我好菜啊都不会
A.Fake NP
给定l和r,求[l,r]中的数的因数中出现次数最多的那一个
sb题 如果l和r相同输出它 不然输出2
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define pa pair<int,int> #define mp(x,y) make_pair(x,y) using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int l,r; int main() { l=read();r=read(); if(r==l)printf("%d\n",l); else puts("2"); return 0; }
B. 3-palindrome
要求构造一个由abc组成的字符串 满足没有长度为3的回文子串的情况下c最少
sb题...发现只用ab构造出aabbaabbaabb....就能满足条件 c根本就用不到。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define pa pair<int,int> #define mp(x,y) make_pair(x,y) using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int n; int main() { n=read(); for(int i=1;i<=n/4;++i) printf("aabb"); n%=4; if(n)printf("a"); if(n>1) printf("a"); if(n>2) printf("b"); return 0; }
C.Find Amir
有n个点,从第i个点到第j个点的费用是(i+j) % (n+1) 求从任意点出发 到达所有点的最小费用
走法1->n->2->n-1->3->n-2...显然最优 输出(n-1)/2即可
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define pa pair<int,int> #define mp(x,y) make_pair(x,y) using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int n; int main() { n=read(); printf("%d\n",(n+1)/2-1); return 0; }
D.Minimum number of steps
给定一个由'a'和'b'组成的串 你要不断的把ab换成bba 直到没有ab 求换多少次,答案取膜10^9+7
打表发现 前面x个a,最后接上去一个b需要的交换次数是2^x -1 并且末尾的a的个数不变 所以直接计算即可
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define pa pair<int,int> #define mp(x,y) make_pair(x,y) #define mod 1000000007 #define MN 1000000 using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int s[MN+5],ans=0; char st[MN+5]; int main() { scanf("%s",st+1); for(int i=1,j=2;i<=MN+1;++i,j=(j<<1)%mod) s[i]=j; for(int i=1,k=0;st[i];++i) if(st[i]=='a') ++k; else (ans+=(k==0?0:s[k]-1))%=mod; printf("%d\n",ans); return 0; }
E. Ice cream coloring
有一棵树,每个节点都有一些1-m的数字,个数和不超过5*10^5
要求你给1-m每个数字一个颜色 满足任意两个相同颜色的没有出现在同一个节点
保证1-m这些数字出现的位置构成原树的一个子图 n,m<=3*10^5
因为这些数字都是连续的 所以假设一个数字在一个节点出现,却没在它的一个儿子节点出现,那么它的那个儿子所在的子树一定没有它
所以直接贪心即可 到达每一个节点的时候给这个节点上的数字中所有已经涂过的颜色打标记 然后给还没涂色的涂尽量小的颜色。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define ld long double #define pa pair<int,int> #define mp(x,y) make_pair(x,y) #define MN 300000 using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } int n,m,head[MN+5],mark[MN+5],ans=1,cnt=0,col[MN+5]; struct edge{int to,next;}e[MN*2+5]; vector<int> v[MN+5]; inline void ins(int f,int t) { e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } void Solve(int x,int fa) { for(int i=0;i<v[x].size();++i) mark[col[v[x][i]]]=x; for(int i=0,j=1;i<v[x].size();++i) { for(;mark[j]==x;++j); if(!col[v[x][i]]) col[v[x][i]]=j++; } for(int i=head[x];i;i=e[i].next) if(e[i].to!=fa) Solve(e[i].to,x); } int main() { n=read();m=read(); for(int i=1;i<=n;++i) { int x=read();ans=max(ans,x); for(int j=1;j<=x;++j) v[i].push_back(read()); } for(int i=1;i<n;++i) ins(read(),read()); Solve(1,0);printf("%d\n",ans); for(int i=1;i<=m;++i) printf("%d ",col[i]?col[i]:1); return 0; }
F.Expected diameter of a tree
给定一个森林,每次询问两棵树,如果随机连接这两棵树上的一个节点,得到的新的树的直径的期望长度。n,m<=10^5
考虑先dp+换根求出所有点为端点在它所在的树上的最长链 同时求出每个树的直径 然后把每棵树上的所有节点的最长链长度排序
查询的时候直接暴力 两个指针推一推(相加必须大于两棵树直径的较大值,否则对答案的贡献就是原树上的直径) (其实二分比较科学)
这样暴力做每次的复杂度是O(n)的 但是发现不同的询问不会太多 所以加一个哈希即可。
期望复杂度大概是根号吧 然后注意在暴力的时候 选择枚举比较小的那棵树 推另一棵树的指针 我没写这个T了一个点(不然应该能混一个第6左右)
大概就是这样吧 第一百多个点T了是最气的
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<cmath> #include<set> #include<map> #define ll long long #define ld long double #define pa pair<int,int> #define mp(x,y) make_pair(x,y) #define MN 100000 using namespace std; inline int read() { int x = 0,f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){if(ch == '-') f = 0;ch = getchar();} while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();} return f?x:-x; } map<ll,double> mp; vector<int> v[MN+5]; int head[MN+5],mx[MN+5],mx2[MN+5],from[MN+5],F[MN+5],f[MN+5],cnt=0,n,m,q,bel[MN+5],tot=0,rt[MN+5]; struct edge{int to,next;}e[MN*2+5]; bool mark[MN+5]; inline void ins(int f,int t) { e[++cnt]=(edge){t,head[f]};head[f]=cnt; e[++cnt]=(edge){f,head[t]};head[t]=cnt; } void Dp(int x,int fa) { mark[x]=1;bel[x]=tot; for(int i=head[x];i;i=e[i].next) if(e[i].to!=fa) { Dp(e[i].to,x);int val=mx[e[i].to]+1; F[x]=max(F[x],F[e[i].to]); if(val>mx[x]) mx2[x]=mx[x],mx[x]=val,from[x]=e[i].to; else if(val>mx2[x]) mx2[x]=val; } F[x]=max(F[x],mx[x]+mx2[x]); } inline void R(int&a,int&b,int&c,int d){if(d>=a)c=a,a=d,b=0;else if(d>=c)c=d;} void Solve(int x,int fa) { v[bel[x]].push_back(mx[x]); for(int i=head[x];i;i=e[i].next) if(e[i].to!=fa) { R(mx[e[i].to],from[e[i].to],mx2[e[i].to],(e[i].to==from[x]?mx2[x]:mx[x])+1); Solve(e[i].to,x); } } void Calc(int x,int y) { x=bel[x];y=bel[y];int Mx=max(F[rt[x]],F[rt[y]]); if(v[x].size()>v[y].size()) swap(x,y); ll ans=0;ll Tot=0,k=v[y][v[y].size()-1]+1; for(int i=0,j=v[y].size()-1;i<v[x].size();++i) { while(j>0&&v[y][j-1]+v[x][i]+1>=Mx) k+=v[y][--j]+1; if(v[x][i]+v[y][j]+1>=Mx) { Tot+=v[y].size()-j; ans+=(ld)k+1LL*(v[y].size()-j)*v[x][i]; } } ll sz=1LL*v[x].size()*v[y].size(); ans+=(sz-Tot)*Mx; if(x>y) swap(x,y); ll ha=1LL*x*(tot+1)+y; printf("%.8lf\n",mp[ha]=(double)ans/sz); } bool check(int x,int y) { x=bel[x],y=bel[y];if(x>y) swap(x,y); ll ha=1LL*x*(tot+1)+y; if(mp[ha]>0) return printf("%.8lf\n",(double)mp[ha]),true; return false; } int main() { n=read();m=read();q=read(); for(int i=1;i<=m;++i) ins(read(),read()); for(int i=1;i<=n;++i) if(!mark[i]) Dp(rt[++tot]=i,0); for(int i=1;i<=tot;++i) Solve(rt[i],0),sort(v[i].begin(),v[i].end()); for(int i=1;i<=q;++i) { int x=read(),y=read(); if(bel[x]==bel[y]) puts("-1"); else if(!check(x,y)) Calc(x,y); } return 0; }