2017杭電ACM集訓隊單人排位賽 - 2 題解
1001,水題,直接模擬即可。比賽中開局連wa三發,因為把int寫成了bool..
1002,積分題,比賽中找到了下面這個積分公式,
但是並沒什麽用,,因為帶入以後存在誤差,估計是展開了以後出現了誤差。然後用自適應simpson即可。大白書上的模板不怎麽好用(雖然能過),優化版的模板如下(本題AC代碼):
1 #include<iostream> 2 #include<cstdio> 3 #include<string> 4 #include<cstring> 5 #include<map> 6 #include<set1002> 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 }
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 題解