2017 Multi-University Training Contest - Team 1 (?/12) 待补
2017-07-25 19:53
501 查看
比赛链接
官方题解
水题,因为他要求位数有多少,所以想到对2^m -1 取10的对数就好了,复杂度O(1) .这里要注意这个-1根本没啥用,无影响.
类似这个oj的这个题 ,不过这个题的范围小,也可以直接搞全排列对其赋值取最大。
官方:
每个字符对答案的贡献都可以看作一个 26 进制的数字,问题相当于要给这些贡献加一个 0 到 25 的权重使得答案最大。最大的数匹配 25,次大的数匹配 24,依次类推。排序后这样依次贪心即可,唯一注意的是不能出现前导
0。
按照权值来贪心,记录每个字母在每个位置上出现的次数,出现的位置越靠前,所分配的数越大,但是也要注意,如果后一位出现的次数加起来大于26则需要向前进位,因为它的前一位也就是比它多26,那么当这个数大于26的时候就相当于他的权重增加了1。
再就是前导0的处理,当有一个字母不能是前导0而我们给他分配为前导0时,就需要注意去贪心的调整使得值尽可能的大,
这里将已经排好序的字母分配的值依次向后调整.
Colorful Tree
题意:
一棵n个结点的树,每个结点都有颜色,定义两点之间的路径长度为路径上出现的不同颜色数目,求树上所有路径
的长度和。一共有
n*(n-1)/2条路径.
思路:
首先,第一想法是我们要考虑每一种颜色对路径的贡献,即:有多少条路径经过了颜色为x的点,最后对于每一种颜色有多少条路径经过它做一个和,就可以得出最后答案.
但是,真正的问题是,对于每一种颜色x,有多少路径经过它我们很难得到,所以想到从反面去想,一共有n*(n-1)/2条路径,最多有n*n*(n-1)/2的和,那么我们去求有多少路径没有经过该颜色,从而得到了有多少路径经过了该颜色.
下面考虑怎么去求有多少路径经过了该颜色,考虑连通块,所有不包含颜色x的连通块中的路径条数都是未经过颜色x的.那么问题就转化成了求不包含颜色x的连通块的大小.我们可以对于每一个点,将其去掉跑一边dfs求连通块的个数,但是这样复杂度明显很高.O(n*n)。我们可以换种思路;
举个例子,如图:
![](http://img.blog.csdn.net/20170726125118786?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvQmFodWlh/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
对于上面这个图,对于当前点1,和他颜色相同的点有1 4 8 7 ,那么怎么找他剩下的连通块的个数呢?首先我们知道,以4 8 7 下面的连通块的路径不可能算在1内,因为经过4 8 7 上来都会有和1一样的颜色了,所以要到他们各自的子树中去分别考虑连通块.对于1我们要找到他所有的子树中,所有颜色和他相同的最高点v,然后减掉这些v中所包含的节点个数,就分别得到了每个子树中不包含颜色1的连通块的点数,从而得到了路径,而这些路径就是不经过1的.
如上图: 当前要判断黑颜色时,先到根节点1,找到他的子树2中最高颜色相同的节点4, 得到节点数为5-2=3,在找到它右边的颜色一样的8,3-1=2 ,得到节点数为2,那么也就是1的以2为根节点的子树中,不包含1的一个联通块我们找到了,节点数为2,一条路径. 同理以3为根节点的颜色相同的最好节点为7,连通块为3 6,节点个数为2,一条路径.
上面我也说过了,对于每一个去跑dfs太麻烦,那么有没有什么好办法呢,这里我采用的是dfs序,将整个树转化成一个序列,然后再进行排序,通过dfs序的每个点控制的左右端点,得到不同的颜色将树划分成了几个连通块.这样一次dfs就可以搞定了.
对于每一种颜色,我们给他内部排个序,让他从最高的根节点开始去子树中求连通块,然后查找该节点所控制的范围内是否存在和根节点颜色相同的点,将其减掉,具体方法和上述例子相同.
不会作图...凑合看吧,尴尬.jpg.
![](http://img.blog.csdn.net/20170726171819518?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG93YXJkRW1pbHk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
假设给你上面这张图,当要求未经过红色的路径有多少时,我们直接将红色排序,直接先找到了四号点,以四号点为根节点找他的子树中 颜色和他相同的最高点,减掉 这些子树中的节点个数,最后得到了连通块,然后再是6 ,7的,但是我们好像落了一些东西吧,没错,我们好像把1 2 3 5这四个点构成的连通块给落下了,这些也是不经过红色点的,所以这时候我们有个小技巧,我们添加一个虚根 0,让0作为这个树的根节点,我让他和所有的颜色都一样,得到如下图
![](http://img.blog.csdn.net/20170726171848857?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvSG93YXJkRW1pbHk=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
我让0号节点指指向1号点(这是一棵树无论以哪个点为根都无所谓,这里我默认从1).当我要查找红色时我就让0这个根节点颜色为红色,这样他的子树必定为1,然后再按照上述步骤,我在1中找到4 5 7 减去他们子树的节点个数,就得到了1 2 3 5 这四个点构成的连通块数了.
大体的方法就是这样,我也不知道我理解的是否正确,反正这一题我看了一上午到现在,涨了很多姿势.
Function
题意:
给出两个序列,一个是0~n-1的排列a,另一个是0~m-1的排列b,现在求满足
![](http://images2017.cnblogs.com/blog/1047463/201707/1047463-20170725211751982-936311996.jpg)
的f的个数。
思路:
假定给你这么一列数
a 2 0 1 ,那么有f函数
f(0)=b[f(a0)]=b[f(2)]
f(2)=b[f(a2)]=b[f(1)]
f(1)=b[f(a1)]=b[f(0)]
那么我们可以看出,当f(0)确定了,那么相应的f1就确定了,f1确定了,f2也就确定了,f2确定了f0确定了,不是一个循环吗?
f0 -f2 -f1-f0 这个确定的是b当中的值,同样的a0-a2-a1-a0这确定的是a中的值,所以这个题目,对于定义域当中的一个循环节,我们在值域b中必须找到和他长度相同的循环节,或者循环节的长度是他的因子.
然后对于每个循环节,找到和他对应的有多少个,最后相乘就好了.
官方题解
1001 | Add More Zero |
#include <cstring> #include <cstdio> #include <algorithm> #include <cmath> using namespace std; int main() { int n, ca = 1; while(~scanf("%d", &n)) { int k = log10(2)*n; printf("Case #%d: %d\n",ca++, k); } return 0; }
Balala Power! |
官方:
每个字符对答案的贡献都可以看作一个 26 进制的数字,问题相当于要给这些贡献加一个 0 到 25 的权重使得答案最大。最大的数匹配 25,次大的数匹配 24,依次类推。排序后这样依次贪心即可,唯一注意的是不能出现前导
0。
按照权值来贪心,记录每个字母在每个位置上出现的次数,出现的位置越靠前,所分配的数越大,但是也要注意,如果后一位出现的次数加起来大于26则需要向前进位,因为它的前一位也就是比它多26,那么当这个数大于26的时候就相当于他的权重增加了1。
再就是前导0的处理,当有一个字母不能是前导0而我们给他分配为前导0时,就需要注意去贪心的调整使得值尽可能的大,
这里将已经排好序的字母分配的值依次向后调整.
#include<bits/stdc++.h> #define MOD 1000000007 using namespace std; const int maxn=1e5+10; typedef long long ll; int n; int num[50];//记录给每个字母分配的值 int lead[50];//前导0 char s[maxn]; ll f[maxn]; void init() { f[0]=1; for(int i=1;i<maxn;i++) { f[i]=f[i-1]*26%MOD; } return ; } struct node { int cnt[maxn];//每个字母在每一位出现的次数..权值 int id; bool operator < (const node &a) const//重载排序 { for(int i = maxn-1; i >= 0; i--) { if(cnt[i] > a.cnt[i]) return 1; else if(cnt[i] < a.cnt[i]) return 0; else ; } } }ch[50]; int main() { int ca=1; init(); while(~scanf("%d",&n)) { memset(ch,0,sizeof(ch)); memset(num,-1,sizeof(num)); memset(lead,0,sizeof(lead)); for(int i=1;i<=n;i++) { scanf(" %s",s); int len=strlen(s); if(len!=1) lead[s[0]-'a']=1; for(int j=0;j<len;j++) { ch[s[j]-'a'].cnt[len-j-1]++;//记录在每一位出现的次数 } } for(int i=0;i<26;i++) { ch[i].id=i; for(int j=0;j<maxn;j++) { if(ch[i].cnt[j]>=26)//判断是否需要进位 { ch[i].cnt[j+1]+=ch[i].cnt[j]/26; ch[i].cnt[j]%=26; } } } sort(ch,ch+26);//这里需要注意,排序后顺序改变,所以加个id,判断为哪个字母. for(int i=0;i<26;i++)//分配值 { num[ch[i].id]=26-i-1; } for(int i=0;i<26;i++) { if(lead[ch[i].id]&&num[ch[i].id]==0)//找前导0 { for(int j=25;j>=0;j--) { if(!lead[ch[j].id]) { for(int k=25;k>j;k--) { num[ch[k].id]=num[ch[k-1].id];//依次调整 } num[ch[j].id]=0; break; } } break; } } ll ans=0; for(int i=0;i<26;i++) { for(int j=0;j<maxn;j++) { ans=(ans+f[j]*ch[i].cnt[j]*num[ch[i].id]%MOD)%MOD; } } printf("Case #%d: ",ca++); printf("%lld\n",ans); } return 0; }
Colorful Tree
题意:一棵n个结点的树,每个结点都有颜色,定义两点之间的路径长度为路径上出现的不同颜色数目,求树上所有路径
的长度和。一共有
n*(n-1)/2条路径.
思路:
首先,第一想法是我们要考虑每一种颜色对路径的贡献,即:有多少条路径经过了颜色为x的点,最后对于每一种颜色有多少条路径经过它做一个和,就可以得出最后答案.
但是,真正的问题是,对于每一种颜色x,有多少路径经过它我们很难得到,所以想到从反面去想,一共有n*(n-1)/2条路径,最多有n*n*(n-1)/2的和,那么我们去求有多少路径没有经过该颜色,从而得到了有多少路径经过了该颜色.
下面考虑怎么去求有多少路径经过了该颜色,考虑连通块,所有不包含颜色x的连通块中的路径条数都是未经过颜色x的.那么问题就转化成了求不包含颜色x的连通块的大小.我们可以对于每一个点,将其去掉跑一边dfs求连通块的个数,但是这样复杂度明显很高.O(n*n)。我们可以换种思路;
举个例子,如图:
对于上面这个图,对于当前点1,和他颜色相同的点有1 4 8 7 ,那么怎么找他剩下的连通块的个数呢?首先我们知道,以4 8 7 下面的连通块的路径不可能算在1内,因为经过4 8 7 上来都会有和1一样的颜色了,所以要到他们各自的子树中去分别考虑连通块.对于1我们要找到他所有的子树中,所有颜色和他相同的最高点v,然后减掉这些v中所包含的节点个数,就分别得到了每个子树中不包含颜色1的连通块的点数,从而得到了路径,而这些路径就是不经过1的.
如上图: 当前要判断黑颜色时,先到根节点1,找到他的子树2中最高颜色相同的节点4, 得到节点数为5-2=3,在找到它右边的颜色一样的8,3-1=2 ,得到节点数为2,那么也就是1的以2为根节点的子树中,不包含1的一个联通块我们找到了,节点数为2,一条路径. 同理以3为根节点的颜色相同的最好节点为7,连通块为3 6,节点个数为2,一条路径.
上面我也说过了,对于每一个去跑dfs太麻烦,那么有没有什么好办法呢,这里我采用的是dfs序,将整个树转化成一个序列,然后再进行排序,通过dfs序的每个点控制的左右端点,得到不同的颜色将树划分成了几个连通块.这样一次dfs就可以搞定了.
对于每一种颜色,我们给他内部排个序,让他从最高的根节点开始去子树中求连通块,然后查找该节点所控制的范围内是否存在和根节点颜色相同的点,将其减掉,具体方法和上述例子相同.
不会作图...凑合看吧,尴尬.jpg.
假设给你上面这张图,当要求未经过红色的路径有多少时,我们直接将红色排序,直接先找到了四号点,以四号点为根节点找他的子树中 颜色和他相同的最高点,减掉 这些子树中的节点个数,最后得到了连通块,然后再是6 ,7的,但是我们好像落了一些东西吧,没错,我们好像把1 2 3 5这四个点构成的连通块给落下了,这些也是不经过红色点的,所以这时候我们有个小技巧,我们添加一个虚根 0,让0作为这个树的根节点,我让他和所有的颜色都一样,得到如下图
我让0号节点指指向1号点(这是一棵树无论以哪个点为根都无所谓,这里我默认从1).当我要查找红色时我就让0这个根节点颜色为红色,这样他的子树必定为1,然后再按照上述步骤,我在1中找到4 5 7 减去他们子树的节点个数,就得到了1 2 3 5 这四个点构成的连通块数了.
大体的方法就是这样,我也不知道我理解的是否正确,反正这一题我看了一上午到现在,涨了很多姿势.
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int mod=1e9+7; const int maxn=2e5+10; int n; int cnt; vector<int>c[maxn],vt[maxn]; int s[maxn]; int in[maxn],out[maxn],f[maxn]; vector<int> ::iterator it; void init() { for(int i=0;i<=n;i++) { c[i].clear(); vt[i].clear(); } } void dfs(int x,int pre)//dfs序 { in[x]=++cnt; s[x]=1; f[x]=pre; for(int i=0;i<vt[x].size();i++) { int v=vt[x][i]; if(v==pre) continue; dfs(v,x); s[x]+=s[v]; } out[x]=cnt; return ; } bool cmp(int x,int y)//重载,左端点的先做根节点. { return in[x]<in[y]; } int main(){ int ca=1; while(~scanf("%d",&n)) { init(); int x,y; for(int i=1;i<=n;i++) { scanf("%d",&x); c[x].push_back(i);//有哪些点对应x这个颜色. } for(int i=1;i<n;i++) { scanf("%d %d",&x,&y); vt[x].push_back(y); vt[y].push_back(x); } cnt=0; vt[0].push_back(1);//添加虚根. dfs(0,-1); //cout<<1<<endl; ll ans=(ll) n*n*(n-1)/2; for(int i=1;i<=n;i++) { if(c[i].empty()) { ans-= (ll)n*(n-1)/2; continue; } c[i].push_back(0);//保证每一种颜色都是从根节点开始去重. sort(c[i].begin(),c[i].end(),cmp); //对于每种颜色排个序. for(int j=0;j<c[i].size();j++) { x=c[i][j]; for(int kk=0;kk<vt[x].size();kk++)//以根节点的每个子节点为根,去寻找最高的颜色相同的点 { y=vt[x][kk]; if(y==f[x])//建图时为双向边,只找x子节点不找x父节点. continue; int size=s[y];//s中存以y为根节点的子树中节点的个数 int k=in[y]; while(1)//在y的子树中查找能否找到和x颜色相同的点. { in[n+1]=k; it=lower_bound(c[i].begin(),c[i].end(),n+1,cmp);//二分查找 if(it == c[i].end()||in[*it]>out[y]) break; size-=s[*it]; k=out[*it]+1; } ans-=(ll)size*(size-1)/2;//减掉不经过x的路径条数. } } } printf("Case #%d: %lld\n",ca++,ans); } return 0; }
Function
题意:
给出两个序列,一个是0~n-1的排列a,另一个是0~m-1的排列b,现在求满足
![](http://images2017.cnblogs.com/blog/1047463/201707/1047463-20170725211751982-936311996.jpg)
的f的个数。
思路:
假定给你这么一列数
a 2 0 1 ,那么有f函数
f(0)=b[f(a0)]=b[f(2)]
f(2)=b[f(a2)]=b[f(1)]
f(1)=b[f(a1)]=b[f(0)]
那么我们可以看出,当f(0)确定了,那么相应的f1就确定了,f1确定了,f2也就确定了,f2确定了f0确定了,不是一个循环吗?
f0 -f2 -f1-f0 这个确定的是b当中的值,同样的a0-a2-a1-a0这确定的是a中的值,所以这个题目,对于定义域当中的一个循环节,我们在值域b中必须找到和他长度相同的循环节,或者循环节的长度是他的因子.
然后对于每个循环节,找到和他对应的有多少个,最后相乘就好了.
#include<bits/stdc++.h> #define Ri(a) scanf("%d", &a) #define Rl(a) scanf("%lld", &a) #define Rf(a) scanf("%lf", &a) #define Rs(a) scanf("%s", a) #define Pi(a) printf("%d\n", (a)) #define Pf(a) printf("%lf\n", (a)) #define Pl(a) printf("%lld\n", (a)) #define Ps(a) printf("%s\n", (a)) #define W(a) while(a--) #define CLR(a, b) memset(a, (b), sizeof(a)) #define MOD 1000000007 #define inf 0x3f3f3f3f #define exp 0.00000001 #define pii pair<int, int> #define mp make_pair #define pb push_back using namespace std; typedef long long ll; const int maxn=1e5+10; ll num1[maxn],num2[maxn]; ll a[maxn],b[maxn]; int main() { int ca=1; int n,m; while(~scanf("%d%d",&n,&m)) { memset(num2,0,sizeof(num2)); for(int i=0;i<n;i++) { scanf("%lld",&a[i]); } for(int i=0;i<m;i++) { scanf("%lld",&b[i]); } int sum=0; for(int i=0;i<n;i++) { ll cnt=0; int k=i; while(a[k] != -1) { cnt++; int t=k; k = a[k]; a[t] = -1; } if(cnt) num1[sum++]=cnt; } for(int i=0;i<m;i++) { ll cnt=0; int k=i; while(b[k]!=-1) { cnt++; int t=k; k=b[k]; b[t]=-1; } if(cnt) num2[cnt]++; } ll ans=1; for(int i=0;i<sum;i++) { ll tt=0; for(int j=1;j*j<=num1[i];j++) { if(num1[i]%j==0) { if(j*j==num1[i]) tt+=num2[j]*j; else { tt += num2[j]*j+num2[num1[i]/j]*num1[i]/j; } } } ans=(ans*tt)%MOD; } printf("Case #%d: ",ca++); printf("%lld\n",ans); } return 0; }
相关文章推荐
- 2017 Multi-University Training Contest - Team 2 待补
- 2017 Multi-University Training Contest - Team 6 【solved:5 / 12】
- 2017 Multi-University Training Contest - Team 9 待补 hdu 6161 ~6170
- 2017 Multi-University Training Contest - Team 1 (5/12)
- 2017 Multi-University Training Contest - Team 7 待补
- 2017 Multi-University Training Contest - Team 5【solved:4 / 12】
- 2017 Multi-University Training Contest - Team 2 1001 Is Derek lying?
- 2017 Multi-University Training Contest - Team 1 Balala Power!
- hdu 6034 Balala Power!(贪心)( 2017 Multi-University Training Contest - Team 1 )(无耻之sort)
- HDU 6038 Function(找规律)——2017 Multi-University Training Contest - Team 1
- 2017 Multi-University Training Contest - Team 1 1006 Function
- HDU6044 & 2017 Multi-University Training Contest - Team 1
- hdu 6047 Maximum Sequence(2017 Multi-University Training Contest - Team 2)
- hdu 6045 Is Derek lying?(2017 Multi-University Training Contest - Team 2)
- hdu 6055 Regular polygon(判断正方形)(2017 Multi-University Training Contest - Team 2)
- hdu 6055 简单计算几何,查找点的四种办法 2017 Multi-University Training Contest - Team 2
- 2017 Multi-University Training Contest - Team 2 Regular polygon
- 2017 Multi-University Training Contest - Team 3 1008 RXD and math
- 2017 Multi-University Training Contest - Team 2
- 2017 Multi-University Training Contest - Team 3 1003 Kanade's sum