(A-G)NEC Programming Contest 2021(AtCoder Beginner 当且仅当有两个#需要判断,其他直接输出YES。当有两个#时,判断这两个#是否在对角线即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=2e5 +7 ;const long long MOD=1e9 +7 ;const double pi=acos (-1 );const double eps=1e-4 ;int main () { fastIO string s[2 ]; cin >>s[0 ]; cin >>s[1 ]; int cnt = 0 ; for (auto ch:s[0 ]) { if (ch=='#' ) cnt++; } for (auto ch:s[1 ]) { if (ch=='#' ) cnt++; } if (cnt==2 ) { if (s[0 ][0 ]==s[1 ][1 ]) cout <<"No" <<endl ; else cout <<"Yes" <<endl ; } else cout <<"Yes" <<endl ; return 0 ; }
模拟题,模拟意义下大数加法,判断是否进位即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=2e5 +7 ;const long long MOD=1e9 +7 ;const double pi=acos (-1 );const double eps=1e-4 ;int main () { fastIO string s1,s2; cin >>s1>>s2; reverse(s1.begin (),s1.end ()); reverse(s2.begin (),s2.end ()); bool hard = false ; for (int i=0 ;i<min (s1.size (),s2.size ());i++) { int x = s1[i] - '0' ; int y = s2[i] - '0' ; if (x+y>=10 ) hard = true ; if (hard) {break ;} } if (hard) cout <<"Hard" <<endl ; else cout <<"Easy" <<endl ; return 0 ; }
贪心题,按照a排序,贪心的从前往后拿即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=3e5 +7 ;const long long MOD=1e9 +7 ;const double pi=acos (-1 );const double eps=1e-4 ; pii a[MAXN];int main () { fastIO int n,w; cin >>n>>w; for (int i=1 ;i<=n;i++) { cin >>a[i].first>>a[i].second; } sort(a+1 ,a+1 +n,greater<pii>()); ll ans = 0 ; for (auto [d,cnt]:a) { if (cnt<=w) { w -= cnt; ans += 1l l*cnt*d; } else { ans += 1l l*w*d; w = 0 ; } if (w==0 ) break ; } cout <<ans<<endl ; return 0 ; }
二分,考虑预处理前缀$pre[]$表示前i个字符有多少个$.$ ,然后对于每个前缀$pre[i]$,在数组$pre[]$中二分第一个大于$pre[i-1]+k$的位置$idx$,然后让$idx—$,也就是最后一个小于等于$pre[i-1]+k$的位置,那么$idx-pre[i]+1$就是从第$i$个字符开始可以做到的最长连续序列的长度。其他位置同理照做,复杂度$O(nlogn)$。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=3e5 +7 ;const long long MOD=1e9 +7 ;const double pi=acos (-1 );const double eps=1e-4 ;int pre[MAXN];int main () { fastIO string s; cin >>s; int n = s.size (); s = "v" + s; int k; cin >>k; for (int i=1 ;i<=n;i++) { pre[i] = pre[i-1 ] + (s[i]=='.' ); } int ans = 0 ; for (int i=1 ;i<=n;i++) { int idx = upper_bound(pre+i,pre+1 +n,k+pre[i-1 ]) - pre; if (idx!=n+1 ) { idx--; ans = max (ans,idx - i + 1 ); } else ans = max (n - i + 1 ,ans); } cout <<ans<<endl ; return 0 ; }
并查集,倒着建立并查集即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=3e5 +7 ;const long long MOD=1e9 +7 ;const double pi=acos (-1 );const double eps=1e-4 ;vector <int >g[MAXN];int p[MAXN]; int find (int x) { if (p[x] != x) p[x] = find (p[x]); return p[x]; }void un (int a,int b) {p[find (a)] = find (b);}int main () { fastIO int n,m; cin >>n>>m; for (int i = 1 ; i <= n; i ++ ) p[i] = i; for (int i=1 ;i<=m;i++) { int x,y; cin >>x>>y; g[x].push_back(y); g[y].push_back(x); } vector <int >ans; int cnt = 0 ; for (int i=n;i>=1 ;i--) { cnt++; for (auto v:g[i]) { if (v>i&&find (i)!=find (v)) {un(i,v);cnt--;} } ans.push_back(cnt); } for (int i=n-2 ;i>=0 ;i--) cout <<ans[i]<<" " ; cout <<"0" <<endl ; return 0 ; }
DP题,希望我们去掉边后是一个二分图,我们可以采用染色的方法来表示二分图,即相邻的两个点颜色总是不同。
可以把中心点$0$设为黑色(用1表示),设为白色(用$0$表示)同理等价。
设状态$dp[i][j][sorce]$表示染色到第$i$个点,将第$i$个点染色为$j$颜色,且顶点$1$的颜色为$sorce$已经删去的边权
然后从点$1$开始染色,顶点1可以是白色/黑色,因此初始状态有两种:$dp[1][0][0]=0,dp[1][1][1] = a[i]$ ,$dp[1][1][1]$由于顶点0也是黑色,如果顶点1也想染成黑色,那么就必须切断$a[1]$这条边。
转移就是四种:
dp[i][0 ][1 ] = min (dp[i-1 ][0 ][1 ]+b[i-1 ],dp[i-1 ][1 ][1 ]); dp[i][1 ][1 ] = min (dp[i-1 ][0 ][1 ]+a[i],dp[i-1 ][1 ][1 ]+a[i]+b[i-1 ]); dp[i][1 ][0 ] = min (dp[i-1 ][0 ][0 ]+a[i],dp[i-1 ][1 ][0 ]+a[i]+b[i-1 ]); dp[i][0 ][0 ] = min (dp[i-1 ][0 ][0 ]+b[i-1 ],dp[i-1 ][1 ][0 ]);
最后计算答案时我们要比较四个:
即$dp[n][1][1]+b[n]$,$dp[n][0][0]+b[n]$(因为顶点1和顶点n颜色一样,那么要断掉外边$b[n]$) 和 $dp[n][1][0],dp[n][0][1]$
min ({dp[n][1 ][1 ]+b[n],dp[n][0 ][0 ]+b[n],dp[n][1 ][0 ],dp[n][0 ][1 ]})
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); #define endl "\n" typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=5e5 +7 ;const long long MOD=998244353 ;const double pi=acos (-1 );const double eps=1e-4 ;vector <int >di = {-1 , 1 , 0 , 0 }, dj = {0 , 0 , -1 , 1 }; ll dp[MAXN][2 ][2 ]; ll a[MAXN],b[MAXN];int main () { fastIO memset (dp,INF,sizeof dp); int n; cin >>n; for (int i=1 ;i<=n;i++) cin >>a[i]; for (int i=1 ;i<=n;i++) cin >>b[i]; dp[1 ][1 ][1 ] = a[1 ]; dp[1 ][0 ][0 ] = 0 ; for (int i=2 ;i<=n;i++) { dp[i][0 ][1 ] = min (dp[i-1 ][0 ][1 ]+b[i-1 ],dp[i-1 ][1 ][1 ]); dp[i][1 ][1 ] = min (dp[i-1 ][0 ][1 ]+a[i],dp[i-1 ][1 ][1 ]+a[i]+b[i-1 ]); dp[i][1 ][0 ] = min (dp[i-1 ][0 ][0 ]+a[i],dp[i-1 ][1 ][0 ]+a[i]+b[i-1 ]); dp[i][0 ][0 ] = min (dp[i-1 ][0 ][0 ]+b[i-1 ],dp[i-1 ][1 ][0 ]); } cout <<min ({dp[n][1 ][1 ]+b[n],dp[n][0 ][0 ]+b[n],dp[n][1 ][0 ],dp[n][0 ][1 ]})<<endl ; return 0 ; }
这题要有一些前置的知识:
首先考虑在一条数轴上,有n个工厂,现在希望在数轴上的找某一点,使得其他工厂到这个点的距离最小,这是最经典的贪心题,也就是绝对值不等式的应用,直接选择n个工厂位置的中位数即可。
现在回到我们这个题上,我们不放考虑二分答案$len$, 对于$check(len)$, 我们可以$O(n)$的枚举每个点最为$len$的中点(运用了上面的贪心性质),然后利用一些维护技巧来$O(1)$的看看左边凑连续$len/2$个$Y$,右边凑连续$len - len/2$个$Y$的花费是否小于$K$。那么这个题就可做到$O(nlogn)$了。
那么剩下的问题就是如何$O(1)$维护左右连续某个数量的$Y$的花费是多少:
对上面图,我们可以维护每个红色点的前缀和,我们可以O(1)查出三根红线长度和$red$,那么利用$3*a[i]-red$就是绿色线段的和,但是我们知道我们不是都移动到a[i],因为如果a[i]前面已经有Y了,那就只能移到这个Y的前面。因此我们其实是多计算了,$1+2+3$的距离。此时我们再减去这段多算的距离,就是左半部全部靠向$a[i]$的花费,此时我们就可以$O(1)$的维护了查询花费了。
右半部同理,但是细节很多,有点麻烦难写。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <bits/stdc++.h> using namespace std ;#define pii pair<int,int> #define pll pair<ll,ll> #define pdd pair<double,double> #define fastIO ios::sync_with_stdio(false);cin.tie(0); #define endl "\n" typedef long long ll;const int INF=0x3f3f3f3f ;const int MAXN=5e5 +7 ;const long long MOD=998244353 ;const double pi=acos (-1 );const double eps=1e-4 ;vector <int >di = {-1 , 1 , 0 , 0 }, dj = {0 , 0 , -1 , 1 };char s[MAXN]; ll k; ll n,m; ll a[MAXN]; ll pre[MAXN];int ans = 0 ;bool check (ll len) { ll lcnt = len/2 +1 ; ll rcnt = len- lcnt; if (len>m) return false ; ll minn = 1e18 ; for (int i=lcnt;i+rcnt<=m;i++) { ll tep = 1l l*lcnt*a[i] - 1l l*(pre[i] - pre[i-lcnt]) - 1l l*(1 +lcnt-1 )*(lcnt-1 )/2 ; ll tep2= 1l l*(pre[i+rcnt] - pre[i]) - 1l l*rcnt*a[i] - 1l l*(1 +rcnt)*rcnt/2 ; tep = tep + tep2; minn=min (minn,tep); } if (minn<=k) return true ; else return false ; }int main () { fastIO string ss; cin >>ss; n = ss.size (); for (int i=0 ;i<ss.size ();i++) s[i+1 ] = ss[i]; cin >>k; for (int i=1 ;i<=n;i++) { if (s[i]=='Y' ) a[++m] = i; } for (int i=1 ;i<=m;i++) pre[i] = pre[i-1 ] + a[i]; if (m==0 ) {cout <<0 <<endl ;return 0 ;} if (m==1 ) {cout <<1 <<endl ;return 0 ;} ll l = 2 ,r = n; while (l<r) { ll mid = l + r + 1 >> 1 ; if (check(mid)) l = mid; else r = mid - 1 ; } if (check(1l l*2 )==false ) cout <<1 <<endl ; else cout <<l<<endl ; return 0 ; }
END