Educational Codeforces Round 58 (Rated for Div. 2) (題解)
阿新 • • 發佈:2019-01-12
C題卡了一個小時, 又被教育場教育了...
大意:求不在$[l,r]$範圍內的最小被$d$整除的數
模擬
#include <iostream> #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; int main() { int t; scanf("%d", &t); REP(i,1,t) { int l,r,d; scanf("%d%d%d", &l, &r, &d);if (d<l) printf("%d\n",d); else printf("%d\n", r/d*d+d); } }
B. Accordion
大意: 給定字串, 可以刪除任意字元, 求最長的的手風琴, 手風琴類似於$[::],[:|:],[:||:],[:|||:]$
直接求出最左端$[:$和最右端$:]$, 再加上中間$|$就行了
#include <iostream> #include <string.h> #define PER(i,a,n) for(int i=n;i>=a;--i) #defineREP(i,a,n) for(int i=a;i<=n;++i) using namespace std; const int N = 5e5+10; char s[N]; int f[N]; int main() { scanf("%s", s+1); int n = strlen(s+1); REP(i,1,n) f[i]=f[i-1]+(s[i]=='|'); int L = 0, R = 0; REP(i,1,n) { if (!L&&s[i]=='[') L = i; if (L&&s[i]==':') { L = i; break; } } PER(i,1,n) { if (!R&&s[i]==']') R = i; if (R&&s[i]==':') { R = i; break; } } if (!L||L>=R) return puts("-1"),0; printf("%d\n",4+f[R]-f[L]); }
大意:給定n個區間, 求劃分為兩個非空集合, 集合間任意兩區間無公共點
注意到只需要找到所有區間的一個分界點$x$即可, 即$x$處無區間覆蓋
然後將$x$左端劃為一個集合, 右端劃為一個集合
可以離散化後差分來覆蓋區間
#include <iostream> #include <algorithm> #include <string.h> #define PER(i,a,n) for(int i=n;i>=a;--i) #define REP(i,a,n) for(int i=a;i<=n;++i) using namespace std; const int N = 5e5+10; char s[N]; int n; int L[N], R[N], f[N], c[N], q[N]; int id(int x) { return lower_bound(f+1,f+1+*f,x)-f; } void work() { scanf("%d", &n); *f = 0; REP(i,1,n) { scanf("%d%d", L+i, R+i); L[i]*=2, R[i]*=2; f[++*f] = L[i]; f[++*f] = L[i]+1; f[++*f] = R[i]; f[++*f] = R[i]+1; } sort(f+1,f+1+*f),*f=unique(f+1,f+1+*f)-f-1; *q = 0; REP(i,1,n) { ++c[id(L[i])],--c[id(R[i]+1)]; q[++*q] = id(L[i]); q[++*q] = id(R[i]+1); } sort(L+1,L+1+n); int sum = 0, ok = 0; REP(i,1,*f) { sum += c[i]; if (!sum) { int t = *lower_bound(L+1,L+1+n,f[i]); if (t>f[i]) { REP(j,1,n) { printf("%d ",R[j]<f[i]?1:2); } ok = 1; printf("\n"); break; } } } if (!ok) puts("-1"); REP(i,1,*q) c[q[i]] = 0; } int main() { int t; scanf("%d", &t); REP(i,1,t) work(); }
D. GCD Counting
大意: 給定樹, 每個結點有一個值, 求樹上最長路徑, 要求滿足路徑上結點的總$gcd>1$
這題最後調了半天發現是子樹間轉移多加了1....
直接對每個結點開$map$暴力轉移$gcd$, $dp[x][y]$表示到$x$結點$gcd=y$時的最大長度
轉移時再暴力統計一下子樹之間兩條鏈的最大值即可
#include <iostream> #include <algorithm> #include <vector> #include <map> #include <string.h> #define PER(i,a,n) for(int i=n;i>=a;--i) #define REP(i,a,n) for(int i=a;i<=n;++i) #define x first #define y second using namespace std; int gcd(int x, int y) {return y?gcd(y,x%y):x;} const int N = 5e5+10; int n, ans; int a[N]; vector<int> g[N]; map<int,int> dp[N]; void dfs(int x, int fa) { for (int y:g[x]) if (y!=fa) { dfs(y,x); //先考慮子樹間轉移 for (auto f1:dp[y]) { int g1 = gcd(f1.x,a[x]); if (g1<=1) continue; for (auto f2:dp[x]) { int g2 = gcd(f2.x,g1); if (g2<=1) continue; ans = max(ans, f1.y+f2.y); } } //最後再用子樹更新x for (auto f1:dp[y]) { int g1 = gcd(f1.x,a[x]); if (g1<=1) continue; ans = max(ans, f1.y+1); dp[x][g1] = max(dp[x][g1], f1.y+1); } } dp[x][a[x]] = max(dp[x][a[x]],1); } int main() { scanf("%d", &n); REP(i,1,n) { scanf("%d", a+i); if (a[i]>1) ans=1; } REP(i,2,n) { int u, v; scanf("%d%d", &u, &v); //這裡優化一下 if (gcd(a[u],a[v])>1) { g[u].push_back(v); g[v].push_back(u); } } REP(i,1,n) { if (dp[i].empty()) dfs(i,0); } printf("%d\n", ans); }
題意: 給若干硬幣, 可以90度旋轉, 詢問是否能將所有放進錢包, 硬幣錢包均為矩形
模擬就行了
#include <iostream> #include <algorithm> #define REP(i,a,b) for(int i=a;i<=b;++i) using namespace std; int main() { int n; scanf("%d", &n); int a=0, b=0; REP(i,1,n) { char op; int x, y; scanf(" %c%d%d", &op, &x, &y); if (x>y) swap(x,y); if (op=='+') a=max(a,x),b=max(b,y); else puts(x>=a&&y>=b?"YES":"NO"); } }
題意: n個城市, 第$i$個城市在位置$a_i$, 第$i$個城市與第$j$個城市相距$|a_i-a_j|$公里 汽車每公里需要$x$汽油, 每個到一個城市可以加滿汽油 給定$m$個汽車, 第$i$輛車從城市$s_i$到$t_i$, 最多加$r_i$次油, 每公里消耗油$c_i$ 每輛汽車容量均為$V$, 初始滿汽油, 求最小的$V$, 使得所有汽車均能到達目的地 $dp[k][i][j]$為預處理出加$k$次油從$i$到$j$時, 每次加油後行駛的最大距離的最小值 可以得到轉移方程$dp[k][i][j]=\min\limits_{i \leq t \leq j} \{ max(dp[k-1][i][t],a_j-a_{t}) \}$ 明顯$dp[k][i][j]$是關於$j$是單增的, 雙指標可以均攤O(1)轉移或者二分O(log)轉移也能過
#include <iostream> #include <algorithm> #define REP(i,a,b) for(int i=a;i<=b;++i) using namespace std; typedef long long ll; const int N = 401; int n, m; int dp[N][N][N]; int a[N]; int main() { scanf("%d%d", &n, &m); REP(i,1,n) scanf("%d", a+i); REP(i,1,n) REP(j,i,n) dp[0][i][j]=a[j]-a[i]; REP(k,1,n) REP(i,1,n) { int now = i; REP(j,i,n) { while (now<j&&max(dp[k-1][i][now],a[j]-a[now])>max(dp[k-1][i][now+1],a[j]-a[now+1])) ++now; dp[k][i][j] = max(dp[k-1][i][now], a[j]-a[now]); } } ll ans = 0; REP(i,1,m) { int s,f,c,r; scanf("%d%d%d%d",&s,&f,&c,&r); ans = max(ans,(ll)dp[r][s][f]*c); } printf("%lld\n", ans); }
G. (Zero XOR Subset)-less 大意: 給定序列$a$, 求將$a$劃分為連續的若干個非空區間, 使得任選若干區間$xor$和均不為$0$, 輸出最大劃分數 $xor$的性質還是太弱了啊, 沒有什麼單調性, 一般直接考慮線性基, 或者$trie$上貪心就好了 首先可以注意到劃分數必然是不超過線性基的 對於總$xor$和為$0$一定不能劃分, 否則貪心的劃分一下就發現一定可以達到線性基大小的 然後就沒了... 程式碼就幾行
#include <iostream> #include <algorithm> #define REP(i,a,b) for(int i=a;i<=b;++i) using namespace std; int a[50], n; int main() { scanf("%d", &n); int sum = 0, t; REP(i,1,n) { scanf("%d", &t); sum ^= t; REP(j,1,*a) t = min(t,t^a[j]); if (t) a[++*a]=t; } printf("%d\n", sum?*a:-1); }