2013 Multi-University Training Contest 8 部分解题报告
problem 1001 (hdu 4676)
Sum Of Gcd
思路:数论问题,gcd(a[i],a[j])一定等于a[i]的某个约数,那么我们就可以枚举a[i]的约数,每个约数在该区间出现的次数,即用该约数*次数,,可得到所求,
但是很重要的一点就是,举例a[i]=8来说,枚举4、2、1,当到4的时候,又需要枚举2,1,那么就会重复计算所以让f(8)+f(4)+f(2)+f(1)=8,可以得到f(n)是欧拉函数,所以可以利用欧拉函数去重,,,,,询问的处理则用分块处理
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<stdlib.h> 6 #include<queue> 7 #include<stack> 8 #include<vector> 9 #include<cmath> 10 #define LL __int64 11 using namespace std; 12 const int maxn=20010; 13 int a[maxn]; 14 int phi[maxn]; 15 LL ans[maxn]; 16 int num[maxn]; 17 vector<int>vec[maxn]; 18 struct node 19 { 20 int l,r; 21 int id; 22 } que[maxn]; 23 int M; 24 bool cmp(struct node a,struct node b) 25 { 26 int aa=a.l/M; 27 int bb=b.l/M; 28 if(aa!=bb) 29 return aa<bb; 30 return a.r<b.r; 31 } 32 void init() 33 { 34 int i,j; 35 vec[0].clear(); 36 for(i=1; i<maxn; i++) 37 { 38 phi[i]=i; 39 } 40 for(i=2; i<maxn; i++) 41 { 42 if(phi[i]==i) 43 { 44 for(j=i; j<maxn; j+=i) 45 phi[j]=phi[j]/i*(i-1);//欧拉函数 46 } 47 48 } 49 for(i=1; i<maxn; i++) 50 { 51 for(j=i; j<maxn; j+=i) 52 { 53 vec[j].push_back(i);//记录j的约数 54 } 55 } 56 } 57 LL run1(int x)//删除 58 { 59 int i; 60 LL res=0; 61 for(i=0; i<vec[x].size(); i++) 62 { 63 num[vec[x][i]]--; 64 res+=phi[vec[x][i]]*num[vec[x][i]]; 65 } 66 return res; 67 } 68 LL run2(int x)//增加 69 { 70 int i; 71 LL res=0; 72 for(i=0; i<vec[x].size(); i++) 73 { 74 res+=phi[vec[x][i]]*num[vec[x][i]]; 75 num[vec[x][i]]++; 76 } 77 return res; 78 } 79 void query(int m) 80 { 81 int i; 82 memset(num,0,sizeof(num));//记录约数影响的数量 83 LL res=0; 84 int l=0,r=0; 85 for(i=1; i<=m; i++) 86 { 87 int li=que[i].l; 88 int ri=que[i].r; 89 while(li<l) 90 { 91 l--; 92 res+=run2(a[l]); 93 94 } 95 while(li>l) 96 { 97 res-=run1(a[l]); 98 l++; 99 } 100 while(ri>r) 101 { 102 r++; 103 res+=run2(a[r]); 104 105 } 106 while(ri<r) 107 { 108 res-=run1(a[r]); 109 r--; 110 } 111 ans[que[i].id]=res; 112 } 113 } 114 int main() 115 { 116 int T,K=0; 117 init(); 118 scanf("%d",&T); 119 while(T--) 120 { 121 int n; 122 int i; 123 K++; 124 scanf("%d",&n); 125 a[0]=0; 126 for(i=1; i<=n; i++) 127 scanf("%d",&a[i]); 128 int m; 129 scanf("%d",&m); 130 for(i=1; i<=m; i++) 131 { 132 scanf("%d%d",&que[i].l,&que[i].r); 133 que[i].id=i; 134 } 135 M=(int)sqrt(n*1.0); 136 sort(que+1,que+m+1,cmp);//分块排序 137 query(m); 138 printf("Case #%d:\n",K); 139 for(i=1; i<=m; i++) 140 { 141 printf("%I64d\n",ans[i]); 142 } 143 } 144 return 0; 145 }
problem 1003 (hdu 4678)
Mine
思路:博弈,,把没有数字的格全部联通,周围包括的有数字的点数为偶数的我们称为偶圈,有奇数个数字点数的圈称为奇圈,
我们可以知道偶圈是不能改变命运的(即谁先点的谁就可以操控让自己最后点)如果是奇圈的话则可以改变命运(即不能操控自己是最后一个点的)
所以我们可以看到偶圈相当于一个数字,如果是奇数个奇圈的话那么第一个点的一定会赢,要是数字的个数和偶圈的个数是奇数的话那么第一个人肯定赢
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<stack> 7 #pragma comment(linker, "/STACK:1024000000,1024000000") 8 const int maxn=1010; 9 int map[maxn][maxn]; 10 bool vis[maxn][maxn]; 11 int dirx[8]= {1,1,1,-1,-1,-1,0,0}; 12 int diry[8]= {1,0,-1,1,0,-1,1,-1}; 13 int n,m,k; 14 int num; 15 using namespace std; 16 bool juge(int x,int y) 17 { 18 if(x<0||x>=n||y<0||y>=m) 19 return false; 20 else 21 return true; 22 } 23 void dfs(int x,int y) 24 { 25 vis[x][y]=true; 26 int i; 27 for(i=0; i<8; i++) 28 { 29 int xx=x+dirx[i]; 30 int yy=y+diry[i]; 31 if(juge(xx,yy)) 32 { 33 if(map[xx][yy]==0&&vis[xx][yy]==false) 34 { 35 dfs(xx,yy); 36 } 37 if(map[xx][yy]>0&&vis[xx][yy]==false) 38 { 39 vis[xx][yy]=true; 40 num++; 41 } 42 } 43 } 44 } 45 int main() 46 { 47 int T,K; 48 while(scanf("%d",&T)!=EOF) 49 { 50 K=0; 51 while(T--) 52 { 53 K++; 54 scanf("%d%d%d",&n,&m,&k); 55 int i,j; 56 int x,y; 57 memset(map,0,sizeof(map)); 58 memset(vis,0,sizeof(vis)); 59 for(i=0; i<k; i++) 60 { 61 scanf("%d%d",&x,&y); 62 map[x][y]=-1; 63 for(j=0; j<8; j++) 64 { 65 int xx=x+dirx[j]; 66 int yy=y+diry[j]; 67 if(juge(xx,yy)) 68 { 69 //printf("x=%d y=%d\n",xx,yy); 70 if(map[xx][yy]!=-1) 71 { 72 map[xx][yy]++; 73 } 74 } 75 } 76 } 77 /*for(i=0;i<n;i++) 78 { 79 for(j=0;j<m;j++) 80 { 81 printf("%d ",map[i][j]); 82 } 83 printf("\n"); 84 }*/ 85 int num1=0,num2=0,nnum=0; 86 for(i=0; i<n; i++) 87 { 88 for(j=0; j<m; j++) 89 { 90 if(map[i][j]==0&&vis[i][j]==false) 91 { 92 num=0; 93 dfs(i,j); 94 if(num%2==0) 95 num2++; 96 else 97 num1++; 98 } 99 } 100 } 101 for(i=0; i<n; i++) 102 { 103 for(j=0; j<m; j++) 104 { 105 if(map[i][j]>0&&vis[i][j]==false) 106 { 107 nnum++; 108 } 109 } 110 } 111 //printf("num1=%d num2=%d nnum=%d\n",num1,num2,nnum); 112 if(num1%2==1||(num2+nnum)%2==1) 113 { 114 printf("Case #%d: Xiemao\n",K); 115 } 116 else 117 { 118 printf("Case #%d: Fanglaoshi\n",K); 119 } 120 } 121 } 122 123 return 0; 124 }
problem 1004 (hdu 4679)
Terrorist’s destroy
思路:(官方题解)
可以枚举删掉的边,通过记录一些值,dp 求出删掉此边后剩下两棵树的直径。
首先一遍dfs 求出以U 为根的子树直径f[U],以及子树中距离U 最长的点和U 的距离,和它是由U 的哪个儿子得到; 同时记录一个次大的,再记录一个次次大的。
然后第二遍dfs,求出除去以U 为根的子树后,剩下的子树的直径h[U]。
如上图:假设已经得到h[U] 了(h[ROOT] = 0),现在要求出除去以V为根的子树后,剩下树的直径h[V ],那么h[V ] 可能由四个方面得到:
1. 2 中h[U]。
2. 1 中U 的儿子的f 最大值。
3. 1 中从U 的儿子出发的一条最长的链+2 中从FA 出发的一条最长链+2(2 中从FA 出发的一条最长链,可以单独维护得到)。
4. 1 中从U 的儿子出发的一条最长的链+1 中从U 的儿子出发的一条次长链+2。
这样复杂度是O(n) 的。
代码好长,,好繁琐
1 #include <iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<queue> 6 #include<stack> 7 #pragma comment(linker, "/STACK:1024000000,1024000000") 8 using namespace std; 9 const int maxn=100010; 10 struct node 11 { 12 int u; 13 int v; 14 int w; 15 int next; 16 int res; 17 } edge[maxn*2]; 18 struct nnode 19 { 20 int m1,x1;//最长路:长度和儿子 21 int m2,x2;//次长路 22 int m3,x3;//次次长路 23 int f1,fx1;//最长链及其儿子 24 int f2,fx2;//次长链及其儿子 25 int len;//最长链 26 int h;//切断后最长链 27 int pre;//祖先最长路 28 } st[maxn]; 29 int lenth[maxn*2]; 30 int cnt; 31 int head[maxn]; 32 bool vis[maxn]; 33 int n; 34 void init() 35 { 36 cnt=0; 37 memset(head,-1,sizeof(head)); 38 memset(vis,0,sizeof(vis)); 39 int i; 40 for(i=0; i<=n; i++) 41 { 42 st[i].m1=st[i].m2=st[i].m3=st[i].x1=st[i].x2=st[i].x3=st[i].len=0; 43 st[i].f1=st[i].fx1=0; 44 st[i].f2=st[i].fx2=0; 45 st[i].pre=0; 46 st[i].h=0; 47 } 48 } 49 void add(int u,int v,int w) 50 { 51 edge[cnt].u=u; 52 edge[cnt].v=v; 53 edge[cnt].w=w; 54 edge[cnt].res=0; 55 edge[cnt].next=head[u]; 56 head[u]=cnt++; 57 58 } 59 void dfs(int s) 60 { 61 int i; 62 vis[s]=1; 63 if(head[s]==-1) 64 { 65 st[s].m1=st[s].m2=st[s].m3=1; 66 st[s].x1=st[s].x2=st[s].x3=-1; 67 st[s].f1=st[s].fx1=0; 68 st[s].f2=st[s].fx2=0; 69 st[s].len=0; 70 return ; 71 } 72 for(i=head[s]; i!=-1; i=edge[i].next) 73 { 74 int v=edge[i].v; 75 if(!vis[v]) 76 { 77 dfs(v); 78 if(st[v].m1>st[s].m1) 79 { 80 st[s].m3=st[s].m2; 81 st[s].m2=st[s].m1; 82 st[s].m1=st[v].m1; 83 st[s].x3=st[s].x2; 84 st[s].x2=st[s].x1; 85 st[s].x1=v; 86 } 87 else 88 { 89 if(st[v].m1>st[s].m2) 90 { 91 st[s].m3=st[s].m2; 92 st[s].m2=st[v].m1; 93 st[s].x3=st[s].x2; 94 st[s].x2=v; 95 } 96 else 97 { 98 if(st[v].m1>st[s].m3) 99 { 100 st[s].m3=st[v].m1; 101 st[s].x3=v; 102 } 103 } 104 } 105 if(st[v].len>st[s].f1) 106 { 107 st[s].f2=st[s].f1; 108 st[s].f1=st[v].len; 109 st[s].fx2=st[s].fx1; 110 st[s].fx1=v; 111 } 112 else if(st[v].len>st[s].f2) 113 { 114 st[s].f2=st[v].len; 115 st[s].fx2=v; 116 } 117 st[s].len=max(st[s].len,st[v].len); 118 } 119 } 120 st[s].m1++; 121 if(st[s].m3!=0) 122 st[s].m3++; 123 if(st[s].m2!=0) 124 { 125 st[s].m2++; 126 st[s].len=max(st[s].len,st[s].m1+st[s].m2-2); 127 } 128 else 129 { 130 st[s].len=max(st[s].len,st[s].m1-1); 131 } 132 } 133 void dfs1(int ss,int s) 134 { 135 vis[s]=true; 136 int i; 137 if(head[s]==-1) 138 return ; 139 for(i=head[s]; i!=-1; i=edge[i].next) 140 { 141 142 int v=edge[i].v; 143 144 if(vis[v]) 145 continue; 146 //printf("u=%d v=%d\n",s,v); 147 /* 148 包含父节点的祖先最长一条路 149 */ 150 if(st[ss].x1!=s) 151 st[s].pre=st[ss].m1; 152 else 153 st[s].pre=st[ss].m2; 154 if(s!=1) 155 { 156 st[s].pre=max(st[s].pre,st[ss].pre+1); 157 } 158 159 //printf("pre=%d\n",st[s].pre); 160 161 //1.除去父节点子树(包括父节点)后的最长链 162 st[v].h=st[s].h; 163 //printf("h[u]=%d\n",st[v].h); 164 //2.其他兄弟的最长链 165 if(st[s].fx1==v) 166 { 167 st[v].h=max(st[v].h,st[s].f2); 168 // printf("其他兄弟最长=%d\n",st[s].f2); 169 } 170 171 else 172 { 173 st[v].h=max(st[v].h,st[s].f1); 174 //printf("其他兄弟最长=%d\n",st[s].f1); 175 } 176 177 178 //3.其他两个兄弟的最长路之和 179 int u; 180 if(v==st[s].x1) 181 { 182 if(st[s].m2!=0) 183 { 184 st[v].h=max(st[v].h,st[ss].pre+st[s].m2-1);//4.祖先的最长路+父亲+其他兄弟的最长路; 185 //printf("pre=%d m2=%d \n",st[ss].pre,st[s].m2); 186 //printf("1.兄弟+祖先=%d\n",st[ss].pre+st[s].m2-1); 187 if(st[s].m3!=0) 188 { 189 u=st[s].m2+st[s].m3-2; 190 } 191 else 192 { 193 u=st[s].m2-1; 194 } 195 } 196 else 197 { 198 st[v].h=max(st[v].h,st[ss].pre);//4.祖先的最长路+父亲+其他兄弟的最长路; 199 //printf("2.兄弟+祖先=%d\n",st[ss].pre); 200 u=0; 201 } 202 st[v].h=max(st[v].h,u); 203 //printf("其他俩兄弟长度之和=%d\n",u); 204 } 205 else 206 { 207 //printf("pre=%d m1=%d ",st[s].pre,st[s].m1); 208 st[v].h=max(st[v].h,st[ss].pre+st[s].m1-1);//4.祖先的最长路+父亲+其他兄弟的最长路; 209 //printf("3.兄弟+祖先=%d\n",st[ss].pre+st[s].m1-1); 210 if(v==st[s].x2) 211 { 212 if(st[s].m3==0) 213 { 214 u=st[s].m1-1; 215 } 216 else 217 { 218 u=st[s].m1+st[s].m3-2; 219 } 220 st[v].h=max(st[v].h,u); 221 // printf("其他俩兄弟长度之和=%d\n",u); 222 } 223 else 224 { 225 u=st[s].m1+st[s].m2-2; 226 st[v].h=max(st[v].h,u); 227 } 228 } 229 //printf("u=%d v=%d h=%d len=%d\n\n\n\n",s,v,st[v].h,st[v].len); 230 231 edge[i].res=max(st[v].h,st[v].len); 232 //printf("i=%d res=%d\n",i,edge[i].res); 233 if(v==st[s].x1) 234 { 235 if(st[s].m2!=0) 236 st[s].pre=max(st[s].pre,st[s].m2); 237 } 238 else 239 { 240 st[s].pre=max(st[s].pre,st[s].m1); 241 } 242 dfs1(s,v); 243 } 244 } 245 int main() 246 { 247 int T,K=0; 248 scanf("%d",&T); 249 while(T--) 250 { 251 K++; 252 int u,v,w; 253 init(); 254 int i; 255 scanf("%d",&n); 256 for(i=0; i<n-1; i++) 257 { 258 scanf("%d%d%d",&u,&v,&w); 259 add(u,v,w); 260 add(v,u,w); 261 } 262 /*printf("cnt=%d\n",cnt); 263 for(i=0;i<cnt;i++) 264 { 265 printf("%d %d %d\n",edge[i].u,edge[i].v,edge[i].w); 266 }*/ 267 dfs(1); 268 /*for(i=1; i<=n; i++) 269 { 270 printf("i=%d : x1=%d m1=%d x2=%d m2=%d x3=%d m3=%d len=%d\n",i,st[i].x1,st[i].m1,st[i].x2,st[i].m2,st[i].x3,st[i].m3,st[i].len); 271 } 272 printf("jif\n\n");*/ 273 memset(vis,0,sizeof(vis)); 274 dfs1(0,1); 275 int ans=1e9+1000; 276 int pos; 277 for(i=0; i<cnt; i++) 278 { 279 //printf("%d=%d ",i/2,edge[i].res); 280 if(edge[i].res>0&&edge[i].res*edge[i].w<ans) 281 { 282 ans=edge[i].res*edge[i].w; 283 pos=i/2; 284 } 285 } 286 printf("Case #%d: %d\n",K,pos+1); 287 } 288 return 0; 289 }
problem 1006(hdu 4681)
String
思路:(官方题解)
首先枚举C 在A 和B 的起始位置,要使A 和B 的公共子序列尽量大,那么C 在A 和B 的占用长度肯定越少越好。所以就分成两个问题:
1. 对A,B 的任意起始位置,求出最近的包含C 的结束位置。先记录一个数组A[i][j](0 i < lenA; 0 j < 26) 保存在i 位置后跟 i 最近的字符j 的距离。这个数组可以O(N2) 暴力枚举出来然后对于每个起始位置i,按C从左往右寻找距离当前位置最近的相应字符即可。
2. 对于任意A,B 的起始位置,求出A,B 在起始位置之前的最长公共子序列,用同样的方法求出与起始位置相应的结束位置以后的最长公共子序列。只需定义dp[i][j] 代表A 的i 位置下B 的j 位置下的最长公共子序列长度。复杂度为O(lenA lenB)。
上面两个问题都预处理后每次查询为O(1)。所以总复杂度为O(N2).
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 using namespace std; 6 const int maxn=1010; 7 int dp1[maxn][maxn]; 8 int dp2[maxn][maxn]; 9 char stra[maxn]; 10 char strb[maxn]; 11 char strc[maxn]; 12 int st1[maxn][2]; 13 int st2[maxn][2]; 14 int main() 15 { 16 //freopen("in.txt","r",stdin); 17 //freopen("out.out","w",stdout); 18 int T,K=0; 19 scanf("%d",&T); 20 getchar(); 21 while(T--) 22 { 23 memset(dp1,0,sizeof(dp1)); 24 memset(dp2,0,sizeof(dp2)); 25 memset(st1,0,sizeof(st1)); 26 memset(st2,0,sizeof(st2)); 27 gets(stra); 28 gets(strb); 29 gets(strc); 30 K++; 31 int lena=strlen(stra); 32 int lenb=strlen(strb); 33 int lenc=strlen(strc); 34 int i; 35 int j; 36 37 for(i=0; i<=lena; i++) 38 { 39 dp1[i][0]=0; 40 } 41 for(j=0; j<=lenb; j++) 42 { 43 dp1[0][j]=0; 44 } 45 for(i=1; i<=lena; i++) 46 { 47 for(j=1; j<=lenb; j++) 48 { 49 if(stra[i-1]==strb[j-1]) 50 dp1[i][j]=dp1[i-1][j-1]+1; 51 else 52 dp1[i][j]=max(dp1[i][j-1],dp1[i-1][j]); 53 } 54 } 55 for(i=0; i<=lena; i++) 56 { 57 dp2[i][lena]=0; 58 } 59 for(j=0; j<=lenb; j++) 60 { 61 dp2[lenb][j]=0; 62 } 63 for(i=lena-1; i>=0; i--) 64 { 65 for(j=lenb-1; j>=0; j--) 66 { 67 if(stra[i]==strb[j]) 68 dp2[i][j]=dp2[i+1][j+1]+1; 69 else 70 dp2[i][j]=max(dp2[i+1][j],dp2[i][j+1]); 71 } 72 } 73 /*for(i=0;i<=lena;i++) 74 { 75 for(j=0;j<=lenb;j++) 76 { 77 printf("%d ",dp1[i][j]); 78 } 79 printf("\n"); 80 } 81 for(i=lena;i>=0;i--) 82 { 83 for(j=lenb;j>=0;j--) 84 { 85 printf("%d ",dp2[i][j]); 86 } 87 printf("\n"); 88 }*/ 89 int mmax=0; 90 int k; 91 int ii,jj,kk; 92 int u=0; 93 for(i=0; i<lena; i++) 94 { 95 if(stra[i]==strc[0]) 96 { 97 for(j=i,k=0; k<lenc&&j<lena;) 98 { 99 if(stra[j]==strc[k]) 100 k++,j++; 101 else 102 j++; 103 } 104 if(k!=lenc) 105 continue; 106 st1[u][0]=i; 107 st1[u][1]=j; 108 u++; 109 } 110 } 111 int uu=0; 112 for(ii=0; ii<lena; ii++) 113 { 114 if(strb[ii]==strc[0]) 115 { 116 for(jj=ii,kk=0; kk<lenc&&jj<lenb;) 117 { 118 if(strb[jj]==strc[kk]) 119 kk++,jj++; 120 else 121 jj++; 122 } 123 if(kk!=lenc) 124 continue; 125 st2[uu][0]=ii; 126 st2[uu][1]=jj; 127 uu++; 128 } 129 } 130 int num; 131 for(i=0; i<u; i++) 132 { 133 for(j=0; j<uu; j++) 134 { 135 num=dp1[st1[i][0]][st2[j][0]]+dp2[st1[i][1]][st2[j][1]]+lenc; 136 if(mmax<num) 137 mmax=num; 138 } 139 } 140 //num=dp1[i][ii]+dp2[j][jj]+lenc; 141 //printf(" [%d] [%d]\n",dp1[i][ii],dp2[j][jj]); 142 printf("Case #%d: %d\n",K,mmax); 143 144 } 145 return 0; 146 }