A http://acm.hdu.edu.cn/showproblem.php?pid=5070
先跳过。
B http://acm.hdu.edu.cn/showproblem.php?pid=5071
维护一个聊天队列,有8种操作,每次操作完要打印对应的信息,认真读题,按题意模拟,一维数组模拟队列就可以,5000操作,交换删除等on暴力复杂度也不会超时。
1 #include<cstdio> 2 #include<cstring> 3 #include<map> 4 using namespace std; 5 const int M=32; 6 char a[M]; 7 char b[M][M]={"","Add","Close","Chat","Rotate","Prior","Choose","Top","Untop"}; 8 map<int,int> mp; 9 int sta[5010],top,cnt,x; 10 void success(){ 11 puts("success."); 12 } 13 void invalid(){ 14 puts("invalid priority."); 15 } 16 void Empty(){ 17 puts("empty."); 18 } 19 void solve1(){ 20 for(int i=0;i<cnt;i++){ 21 if(sta[i]==x){ 22 puts("same priority."); 23 return ; 24 } 25 } 26 sta[cnt++]=x; 27 success(); 28 } 29 void solve2(){ 30 for(int i=0;i<cnt;i++){ 31 if(sta[i]==x){ 32 if(top==x) top=-1; 33 for(int j=i;j<cnt-1;j++){ 34 sta[j]=sta[j+1]; 35 } 36 cnt--; 37 printf("close %d with %d. ",x,mp[x]); 38 mp.erase(x); 39 return ; 40 } 41 } 42 invalid(); 43 } 44 void solve3(){ 45 if(!cnt){ 46 Empty(); 47 return ; 48 } 49 if(top!=-1){ 50 mp[top]+=x; 51 success(); 52 return ; 53 } 54 mp[sta[0]]+=x; 55 success(); 56 } 57 void solve4(int id){ 58 if(id<1||id>cnt){ 59 puts("out of range."); 60 return ; 61 } 62 int tmp=sta[id-1]; 63 for(int i=id-1;i>0;i--){ 64 sta[i]=sta[i-1]; 65 } 66 sta[0]=tmp; 67 success(); 68 } 69 void solve5(){ 70 if(!cnt){ 71 Empty(); 72 return ; 73 } 74 int id=0; 75 for(int i=0;i<cnt;i++){ 76 if(sta[i]>sta[id]){ 77 id=i; 78 } 79 } 80 solve4(id+1); 81 } 82 void solve6(){ 83 for(int i=0;i<cnt;i++){ 84 if(sta[i]==x){ 85 solve4(i+1); 86 return ; 87 } 88 } 89 invalid(); 90 } 91 void solve7(){ 92 for(int i=0;i<cnt;i++){ 93 if(sta[i]==x){ 94 top=x; 95 success(); 96 return ; 97 } 98 } 99 invalid(); 100 } 101 void solve8(){ 102 if(top!=-1){ 103 top=-1; 104 success(); 105 return ; 106 } 107 puts("no such person."); 108 } 109 int main(){ 110 int t,n; 111 while(~scanf("%d",&t)){ 112 while(t--){ 113 scanf("%d",&n); 114 int cas=1; 115 top=-1,cnt=0; 116 mp.clear(); 117 while(n--){ 118 scanf("%s",a); 119 int id=1; 120 for(int i=1;i<=8;i++){ 121 if(!strcmp(a,b[i])){ 122 id=i; 123 break; 124 } 125 } 126 if(id!=5&&id!=8){ 127 scanf("%d",&x); 128 } 129 printf("Operation #%d: ",cas++); 130 if(id==1){ 131 solve1(); 132 } 133 else if(id==2){ 134 solve2(); 135 } 136 else if(id==3){ 137 solve3(); 138 } 139 else if(id==4){ 140 solve4(x); 141 } 142 else if(id==5){ 143 solve5(); 144 } 145 else if(id==6){ 146 solve6(); 147 } 148 else if(id==7){ 149 solve7(); 150 } 151 else{ 152 solve8(); 153 } 154 } 155 if(top!=-1&&mp[top]){ 156 printf("Bye %d: %d ",top,mp[top]); 157 } 158 for(int i=0;i<cnt;i++){ 159 if(top!=sta[i]&&mp[sta[i]]){ 160 printf("Bye %d: %d ",sta[i],mp[sta[i]]); 161 } 162 } 163 } 164 } 165 return 0; 166 }
C http://acm.hdu.edu.cn/showproblem.php?pid=5072
输入n个数,n<=10^5,其中选出3个数,满足两两之间互质,或者两两之间都不互质,问有多少种不同的选法。
解法:暴力n^3,然后加3次gcd判断,统计。明显超时,但可以用来测测。
根据 模型请参考 《算法竞赛入门经典 训练指南》 p105 问题6,大白书。
那个模型是这样的,给定空间n个点n==1000,没有3点共线,每两个点之间都用红色或黑色边连接,求3条边同色的三角形个数。
那么对于这个题,我们可以把每个数当做三角形的点,数与数之间互质当做红边,不互质当做黑边,那么就是这个问题了。
三角形这个题,可以求反面,求出非单色三角形,就可以用总数扣去得到答案。对于非单色三角形,必然有两个点连的两条边不同色。
所以对每个点,如果有ai条红色边,就有n-1-ai条蓝色边,ai*(n-1-ai)就是该点形成的非单色三角形,因为一个三角形会算两次,(必然有两个点连的两条边不同色)。所以除二。
对于上面这个题,一样的方法,对每个数,求出有多少个互质的,假设求出来是bi,那么不互质的就是(n-1-bi),那么这个数能贡献的答案就是bi*(n-1-bi),因为每种情况会重复算一次,所以最后答案除2就是不合法的情况,用总数减去就是题目所求。
问题只剩下如何求bi,多少个互质的,然后就是二进制暴力质因子,容斥原理++--,理解一下。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 typedef long long LL; 7 const int M=1e5+10; 8 int pri[M],mark[M],pricnt;//mark[i]存i的最小因子,素数时mark[i]==i 9 void sieve_primes() { //筛素数 10 pricnt=0; 11 mt(mark,0); 12 mark[0]=mark[1]=1; 13 for(int i=2; i<M; i++) { 14 if(!mark[i]) pri[pricnt++]=mark[i]=i; 15 for(int j=0; pri[j]*i<M; j++) { 16 mark[i*pri[j]]=pri[j]; 17 if(!(i%pri[j])) break; 18 } 19 } 20 } 21 int fac[128]; 22 int find_fac_before_sieve(int n) { //筛完素数后用,n<M 23 int cnt=0; 24 while(mark[n]!=1) { 25 fac[cnt++]=mark[n]; 26 n/=mark[n]; 27 } 28 return cnt; 29 } 30 int num[M]; 31 int lf; 32 void dfs(int now,int sum){ 33 if(now==lf){ 34 num[sum]++; 35 return ; 36 } 37 dfs(now+1,sum); 38 dfs(now+1,sum*fac[now]); 39 } 40 LL help; 41 void dfs_ans(int now,int sum,int cnt){ 42 if(now==lf){ 43 // if(!cnt) return ; 44 if(cnt&1) help-=num[sum]; 45 else help+=num[sum]; 46 return ; 47 } 48 dfs_ans(now+1,sum,cnt); 49 dfs_ans(now+1,sum*fac[now],cnt+1); 50 } 51 int a[M]; 52 int main(){ 53 sieve_primes(); 54 int t,n; 55 while(~scanf("%d",&t)){ 56 while(t--){ 57 scanf("%d",&n); 58 for(int i=0;i<n;i++){ 59 scanf("%d",&a[i]); 60 } 61 mt(num,0); 62 for(int i=0;i<n;i++){ 63 lf=find_fac_before_sieve(a[i]); 64 lf=unique(fac,fac+lf)-fac; 65 dfs(0,1); 66 } 67 LL ans=0; 68 for(int i=0;i<n;i++){ 69 lf=find_fac_before_sieve(a[i]); 70 lf=unique(fac,fac+lf)-fac; 71 help=0; 72 dfs_ans(0,1,0); 73 if(a[i]==1) help--; 74 ans+=help*(n-1-help); 75 } 76 printf("%lld ",1LL*n*(n-1)*(n-2)/6-ans/2); 77 } 78 } 79 return 0; 80 }
D http://acm.hdu.edu.cn/showproblem.php?pid=5073
输入一维数轴上n个坐标,每个点重量是1,可以允许最多移动k个点,允许移动到同一个位置,问如何移动使得(每个点到重心的距离的平方)的和最小。
因为重量都是1,所以重心就会落在n个坐标的平均值位置。
一个先决条件是,k个点一定都要移动,那么剩下n-k个点去求那个权值。
因为移动的k个点可以都移动到剩下点的重心去,那么他们对权值的贡献就是零,相当于直接删去。
还有一个条件是,为了使得大家距离重心更近,那么肯定留下的n-k个点要连续,这样肯定比离散的距离重心近。
所以我们将坐标排序,枚举起点,求长度为n-k这一段的权值,最后取最小值。
问题就需要尽快的求出一个连续段的权值。
对于距离平方求和,就是(Xi-Xavg)^2=Xi^2-2*Xi*Xavg+Xavg^2 (L<=i<=R)
求均值预处理前n项和即可,第一项就预处理前n项平方和。
1 #include<cstdio> 2 #include<algorithm> 3 using namespace std; 4 const int M=5e4+10; 5 int a[M]; 6 int main(){ 7 int t,n,m; 8 while(~scanf("%d",&t)){ 9 while(t--){ 10 scanf("%d%d",&n,&m); 11 for(int i=0;i<n;i++){ 12 scanf("%d",&a[i]); 13 } 14 if(n==m){ 15 puts("0"); 16 continue; 17 } 18 sort(a,a+n); 19 double sum=0,sum2=0; 20 int cnt=n-m; 21 for(int i=0;i<cnt;i++){ 22 sum+=a[i]; 23 sum2+=1.0*a[i]*a[i]; 24 } 25 double ans=sum2-2*(sum/cnt)*sum+sum/cnt*sum; 26 for(int i=cnt;i<n;i++){ 27 sum+=a[i]; 28 sum-=a[i-cnt]; 29 sum2+=1.0*a[i]*a[i]; 30 sum2-=1.0*a[i-cnt]*a[i-cnt]; 31 double now=sum2-2*(sum/cnt)*sum+sum/cnt*sum; 32 ans=min(ans,now); 33 } 34 printf("%.10f ",ans); 35 } 36 } 37 return 0; 38 }
E http://acm.hdu.edu.cn/showproblem.php?pid=5074
输入n个数的序列,序列的得分是ai和ai+1之间贡献的,也就是有n-1个得分。数字一定是1到m的,输入会给出m*m的矩阵,即矩阵mat【i】【j】 表示 i 在 j 前面的得分。序列中值为1到m的是必须选择该值,值为-1的是你可以在1到m中任意选一个放在该位置,最后要求序列可能得到的最大分数。
解法:若枚举,如果n个都是-1,每一个有1到m种情况,总情况数就是m的n次方,50^100,观察发现,有最优子结构性质,即dp【n】【m】表示前n个以m为结尾所能得到的最大分数。
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #define mt(a,b) memset(a,b,sizeof(a)) 5 using namespace std; 6 const int M=64; 7 int dp[M<<1][M],a[M][M],b[M<<1]; 8 int main(){ 9 int t,n,m; 10 while(~scanf("%d",&t)){ 11 while(t--){ 12 scanf("%d%d",&n,&m); 13 for(int i=1;i<=m;i++){ 14 for(int j=1;j<=m;j++){ 15 scanf("%d",&a[i][j]); 16 } 17 } 18 for(int i=1;i<=n;i++){ 19 scanf("%d",&b[i]); 20 } 21 mt(dp,-1); 22 if(b[1]==-1){ 23 for(int j=1;j<=m;j++){ 24 dp[1][j]=0; 25 } 26 } 27 else{ 28 dp[1][b[1]]=0; 29 } 30 for(int i=1;i<n;i++){ 31 for(int j=1;j<=m;j++){ 32 if(dp[i][j]==-1) continue; 33 if(b[i+1]>0){ 34 dp[i+1][b[i+1]]=max(dp[i+1][b[i+1]],dp[i][j]+a[j][b[i+1]]); 35 continue; 36 } 37 for(int k=1;k<=m;k++){ 38 dp[i+1][k]=max(dp[i+1][k],dp[i][j]+a[j][k]); 39 } 40 } 41 } 42 int ans=0; 43 for(int j=1;j<=m;j++){ 44 ans=max(ans,dp[n][j]); 45 } 46 printf("%d ",ans); 47 } 48 } 49 return 0; 50 }
I http://acm.hdu.edu.cn/showproblem.php?pid=5078
输入n个点,每个点有时间t,坐标x,y。定义相邻两点之间的难度为距离/时间差,求最大的难度。
解法:按照定义算相邻两个的难度,取最大值。
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 const int M=1e3+10; 6 struct G{ 7 double t,x,y; 8 }g[M]; 9 double f(G a,G b){ 10 return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))/(b.t-a.t); 11 } 12 int main(){ 13 int t,n; 14 while(~scanf("%d",&t)){ 15 while(t--){ 16 scanf("%d",&n); 17 for(int i=0;i<n;i++){ 18 scanf("%lf%lf%lf",&g[i].t,&g[i].x,&g[i].y); 19 } 20 double ans=f(g[0],g[1]); 21 for(int i=2;i<n;i++){ 22 ans=max(ans,f(g[i-1],g[i])); 23 } 24 printf("%.10f ",ans); 25 } 26 } 27 return 0; 28 }