2014ACM/ICPC亚洲区鞍山站 清华命题

http://acm.hdu.edu.cn/showproblem.php?pid=5070

先跳过。

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code

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 }
View Code
原文地址:https://www.cnblogs.com/gaolzzxin/p/4658724.html