1. 程式人生 > >2017杭電ACM集訓隊單人排位賽 - 2 題解

2017杭電ACM集訓隊單人排位賽 - 2 題解

需要 6.0 hdu2045 owb style 適應 freopen count 枚舉

  1001,水題,直接模擬即可。比賽中開局連wa三發,因為把int寫成了bool..

  1002,積分題,比賽中找到了下面這個積分公式,

技術分享

  但是並沒什麽用,,因為帶入以後存在誤差,估計是展開了以後出現了誤差。然後用自適應simpson即可。大白書上的模板不怎麽好用(雖然能過),優化版的模板如下(本題AC代碼):

技術分享
 1 #include<iostream> 
 2 #include<cstdio> 
 3 #include<string> 
 4 #include<cstring> 
 5 #include<map> 
 6 #include<set
> 7 #include<queue> 8 #include<stack> 9 #include<ctime> 10 #include<algorithm> 11 #include<cmath> 12 #include<vector> 13 #include<list> 14 #include<fstream> 15 #include<sstream> 16 #include<cassert> 17 #include<bitset> 18
#define showtime printf(stderr,"time = %.15f\n",clock() / (double)CLOCKS_PER_SEC) 19 #pragma comment(linker, "/STACK:1024000000,1024000000") 20 using namespace std; 21 typedef long long ll; 22 typedef long long LL; 23 typedef unsigned long long ull; 24 #define MP make_pair 25 #define PII pair<int,int> 26
#define PFI pair<double,int> 27 #define PLL pair<ll,ll> 28 #define lson l,mid,rt<<1 29 #define rson mid+1,r,rt<<1|1 30 //freopen("data.in","r",stdin); 31 //freopen("data.out","w",stdout); 32 typedef vector<long long> vec; 33 typedef vector<vec> mat; 34 inline int popcnt(int x){return __builtin_popcount(x); } 35 inline int clz(int x){return __builtin_clz(x);} //末尾的 0,即對 lowbit 取log 36 inline int clz(LL x){return __builtin_clzll(x);} 37 inline int lg2(int x){ return !x ? -1 : 31-clz(x);} 38 inline int lg2(LL x){ return !x ? -1 : 63-clz(x);} 39 inline int __lcm(int x, int y){ return x / __gcd(x, y) * y; } 40 const double eps = 1e-6; 41 const double PI = acos(-1.); 42 const double E = 2.71828182845904523536; 43 const int MOD = (int)1e9+7; 44 const int INF = 0x3f3f3f3f; 45 const ll INFLL = 0x3f3f3f3f3f3f3f3f; 46 const ull BAS = 233; 47 const int M = 1e5 + 7; 48 const int N = 1e5 + 7; 49 50 double v1, v2, x, k; 51 52 double f(double t){ 53 // 表達式 54 return k / (((x-v2*t)*(x-v2*t)) + (t*v1) * (t*v1)); 55 } 56 double simpson(double l,double r){ // simpson公式 57 return 1.0 / 6.0 * (r - l) * (f(l) + 4 * f((l+r)/2.0) + f(r)); 58 } 59 double integral(double l,double r){ 60 double mid = (l + r) / 2.0; 61 double ret = simpson(l,r); // 二分逼近 62 if(fabs(ret - simpson(l,mid)-simpson(mid,r)) < eps) 63 return ret; 64 else 65 return integral(l,mid) + integral(mid,r); 66 } 67 /* 68 k 69 --------------------- 70 (x-v2*t)^2 + (t*v1)^2 71 */ 72 int main(){ 73 int T; 74 cin >> T; 75 while(T --){ 76 cin >> v1 >> v2 >> x >> k; 77 printf("%.2f\n", integral(0, 1e18)); 78 } 79 return 0; 80 }
1002

  

  1003,用dfs不能過,因為有環,那麽誰先更新誰後更新對答案有偏差,因此采用優先隊列的dij來做。代碼如下:

技術分享
 1 #include <bits/stdc++.h>
 2 #define t_mid (l+r>>1)
 3 #define ls (o<<1)
 4 #define rs (o<<1|1)
 5 #define lson ls,l,t_mid
 6 #define rson rs,t_mid+1,r
 7 using namespace std;
 8 const int N = 1e5 + 100;
 9 typedef long long ll;
10 typedef pair<int,int> pii;
11 const int mod = 1e9 + 7;
12 
13 int T;
14 int n, m;
15 int a[N];
16 vector<pii> G[N];
17 
18 int main()
19 {
20     scanf("%d",&T);
21     while(T--)
22     {
23         scanf("%d%d",&n,&m);
24         priority_queue<pii,vector<pii>,greater<pii> > Q;
25         for(int i=1;i<=n;i++) {scanf("%d",a+i); Q.push(pii(a[i], i)); G[i].clear();}
26         for(int i=1;i<=m;i++)
27         {
28             int x, y, z;
29             scanf("%d%d%d",&x,&y,&z);
30             G[x].push_back(pii(y,z));
31             G[y].push_back(pii(x,z));
32         }
33         while(!Q.empty())
34         {
35             pii p = Q.top(); Q.pop();
36             int u1 = p.second;
37             for(pii pp : G[u1])
38             {
39                 int u2 = pp.first;
40                 int v = pp.second;
41                 if(a[v] > a[u1] + a[u2])
42                 {
43                     a[v] = a[u1] + a[u2];
44                     Q.push(pii(a[v], v));
45                 }
46             }
47         }
48         for(int i=1;i<=n;i++) printf("%d%c",a[i],i==n?\n: );
49     }
50     return 0;
51 }
1003

  1004,水題。

  1005,3!枚舉一下3種字符的排列順序,得到此時的目標串,然後和原串匹配一下得到在目標串中的連續的最長的子串,這個長度就是不需要動的牌,然後所有情況比較得到最優解即可。需要是連續的是因為每次只能把牌放到最前和最後,不難證明可以成立。同時這個匹配的方法可以dp,也可以暴力。這個dp的轉移還是需要註意一下的。代碼如下:

技術分享
  1 #include <bits/stdc++.h>
  2 #define t_mid (l+r>>1)
  3 #define ls (o<<1)
  4 #define rs (o<<1|1)
  5 #define lson ls,l,t_mid
  6 #define rson rs,t_mid+1,r
  7 using namespace std;
  8 const int N = 1e5 + 100;
  9 typedef long long ll;
 10 typedef pair<int,int> pii;
 11 const int mod = 1e9 + 7;
 12 
 13 int T;
 14 char s[20];
 15 char t[20];
 16 int cnt[4];
 17 char need[4][20];
 18 char str[60];
 19 int ans;
 20 
 21 int dp[20][20];
 22 void solve()
 23 {
 24     // s -> str
 25     //printf("%s -- %s\n",s+1,str+1);
 26     for(int l=1;l<=13;l++)
 27     {
 28         for(int r=l+1;r<=13;r++)
 29         {
 30             int temp = 0;
 31             for(int i=1;i<=13;i++)
 32             {
 33                 if(s[i] == str[l+temp])
 34                 {
 35                     temp++;
 36                 }
 37             }
 38             if(temp == r - l + 1) ans = max(ans, r - l + 1);
 39         }
 40     }
 41     /*memset(dp,0,sizeof dp);
 42     for(int i=1;i<=13;i++)
 43     {
 44         for(int j=1;j<=13;j++)
 45         {
 46             if(s[i] == str[j])
 47             {
 48                 dp[i][j] = dp[i-1][j-1] + 1;
 49             }
 50             else dp[i][j] = dp[i-1][j];
 51             ans = max(ans, dp[i][j]);
 52         }
 53     }*/
 54 }
 55 
 56 int main()
 57 {
 58     // 3 1 2
 59     //printf("%d %d %d ?\n",‘a‘,‘1‘,‘A‘);
 60     scanf("%d",&T);
 61     while(T--)
 62     {
 63         scanf("%s",s+1);
 64         memcpy(t,s,sizeof t);
 65         sort(t+1,t+1+13);
 66         memset(cnt,0,sizeof cnt);
 67         memset(need,0,sizeof need);
 68         int now = 1;
 69         int p = 0;
 70         for(;;now++)
 71         {
 72             if(!isdigit(t[now])) break;
 73             need[1][++p] = t[now];
 74         }
 75         cnt[1] = now - 1;
 76         p = 0;
 77         for(;;now++)
 78         {
 79             if(t[now] < A || t[now] > Z) break;
 80             need[2][++p] = t[now];
 81         }
 82         cnt[2] = p;
 83         p = 0;
 84         for(;now<=13;now++)
 85         {
 86             need[3][++p] = t[now];
 87         }
 88         cnt[3] = p;
 89 
 90         ans = 1;
 91         memset(str,0,sizeof str);
 92         strcat(str+1,need[1]+1); strcat(str+1,need[2]+1); strcat(str+1,need[3]+1);
 93         solve();
 94         memset(str,0,sizeof str);
 95         strcat(str+1,need[1]+1); strcat(str+1,need[3]+1); strcat(str+1,need[2]+1);
 96         solve();
 97         memset(str,0,sizeof str);
 98         strcat(str+1,need[2]+1); strcat(str+1,need[1]+1); strcat(str+1,need[3]+1);
 99         solve();
100         memset(str,0,sizeof str);
101         strcat(str+1,need[2]+1); strcat(str+1,need[3]+1); strcat(str+1,need[1]+1);
102         solve();
103         memset(str,0,sizeof str);
104         strcat(str+1,need[3]+1); strcat(str+1,need[1]+1); strcat(str+1,need[2]+1);
105         solve();
106         memset(str,0,sizeof str);
107         strcat(str+1,need[3]+1); strcat(str+1,need[2]+1); strcat(str+1,need[1]+1);
108         solve();
109         printf("%d\n",13-ans);
110     }
111     return 0;
112 }
1005

  1006,做這題前可以先做一下hdu2045。在該題中兩個顏色相同的位置(稱之為分隔處)把這個串分成了兩部分,這兩個相同顏色的位置假設顏色已經固定了(最後只要把答案再乘以k即可),那麽剩余的有(k-1)種顏色可選擇,設f[i]為長度為i的合理串的種數,那麽這個狀態可以從以下兩個狀態轉移過來:

  1.長度為i-1的合理的串,那麽其最後一個顏色一定與分隔處不同,那麽第i個位置只剩下了(k-2)種選擇,貢獻為(k-2)*f[i-1]。

  2.長度為i-1的不合理的串,那麽前i-2個必須合理,否則不能轉移到i處,同時不可能是i-2和i-1處的顏色不同(如果不同也無法轉移到i處),只能是i-1處和分隔處的顏色相同才行,那麽i這個地方有(k-1)種選擇,貢獻為(k-1)*f[i-2]。

  綜上可得,f[i] = f[i-1]*(k-2) + f[i-2]*(k-1)(可以發現這種推算f的方式和hdu2045中類似)。邊界處手算一下即可。得到了以後就可以做了。假設分隔位置為l和r,那麽新的兩部分的長度為別為Len1=|r-l|-1,Len2=n-|r-l|-1。那麽最後的答案為k*f[Len1]*f[Len2] % mod。

  同時,還有別的方法推f數組,每次擴張一個新長度時,只有k-1種選擇(因為不能和分隔處顏色相同),所以總次數為(k-1)^i,這其中有重復的,計算方法如下:如果i-1處和分隔處顏色相同,那麽i這個位置一定不會和相鄰位置顏色相同;當i-1處和分隔處顏色不同,這時才有可能i的顏色和i-1處的重合,這種情況的總數是,前i-1個位置的正確總數,即f[i-1],那麽f[i] = (k-1)^i - f[i-1]。後面的計算方法類似。

  代碼如下(第一種推算f數組的方法):

技術分享
 1 #include <bits/stdc++.h>
 2 #define t_mid (l+r>>1)
 3 #define ls (o<<1)
 4 #define rs (o<<1|1)
 5 #define lson ls,l,t_mid
 6 #define rson rs,t_mid+1,r
 7 using namespace std;
 8 const int N = 1e5 + 100;
 9 typedef long long ll;
10 typedef pair<int,int> pii;
11 const int mod = 1e9 + 7;
12 
13 int n,m,k;
14 int f[N];
15 
16 int main()
17 {
18     while(scanf("%d%d%d",&n,&m,&k) == 3)
19     {
20         f[1] = k-1;
21         f[2] = (ll)(k-1) * (k-2) % mod;
22         for(int i=3;i<=n;i++)
23         {
24             f[i] = ((ll)(k-2)*f[i-1] + (ll)(k-1)*f[i-2]) % mod;
25         }
26         while(m--)
27         {
28             int l, r;
29             scanf("%d%d",&l,&r);
30             int ans = (ll)k*f[abs(r-l)-1] % mod * (f[n-abs(r-l)-1]) % mod;
31             printf("%d\n",ans);
32         }
33     }
34     return 0;
35 }
1006

  1007,分奇數和偶數作為起點進行掃描,維護遇到的最小的前綴和並更新答案即可。

  1008,結論題。如果兩點是相鄰的則能追到,否則不行。理由是如果距離大於等於2,走的人走哪裏,打斷下一條路即可。

  1009,分情況討論即可。註意即使一開始全部相同,也必須要交換(也就是說要滿足,只能換相同的兩個字母)。

  1010,因為位數少,直接暴力即可。如果位數多,可以考慮數位dp。

技術分享

2017杭電ACM集訓隊單人排位賽 - 2 題解