2022/3/13 std
A:簽到題
排個序看一下相鄰三個能夠構成三角形即可。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e4 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; intView Coden; LL a[N]; void solve() { cin >> n; for(int i = 1;i <= n;++i) cin >> a[i]; sort(a + 1,a + n + 1); int f = 0; for(int i = 1;i <= n - 2;++i) { LL tmp = a[i] + a[i + 1]; if(tmp > a[i + 2]) f = 1; } printf("%s\n",f ? "possible" : "impossible"); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} //system("pause"); return 0; }
B:簽到題
map標記一下男孩和女孩的位置即可計算
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5 + 5; constView Codeint M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,x[N],y[N]; map<int,int> boy,girl; void solve() { cin >> n >> m; rep(i,1,n) { scanf("%d",&x[i]); boy[x[i]] = i; } rep(i,1,m) { scanf("%d",&y[i]); girl[y[i]] = i; } int q;cin >> q; while(q--) { int x;scanf("%d",&x); if(girl[x] != 0) printf("Girl %d\n",girl[x]); else { if(boy[x] != 0) printf("Boy %d\n",m + boy[x]); else printf("Sorry\n"); } } } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} //system("pause"); return 0; }
C:先序還原一下樹,然後按層輸出一下即可。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; string s; int now = -1,mx = 0; vector<char> dep[N]; char dfs(int x,int d) { ++now; if(s[now] == '.') return '.'; mx = max(mx,d); dep[d].push_back(s[now]); dfs(x >> 1,d + 1); dfs(x >> 1 | 1,d + 1); } void solve() { cin >> s; dfs(1,0); rep(i,0,mx) { for(auto v : dep[i]) { printf("%c",v); } } } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; }View Code
D:考慮分治的思想,對於段[L,r],當先手的人能操作的上限是up1,後手的人能操作的上限是up2時是否有必勝的方法。
顯然,因為我們每次只能取兩段,那麼很顯然就是看區間[L + 1,r],[L,r - 1]的情況。
若兩個子區間裡有一個必敗策略,那麼先手必定要選讓後手失敗。
否則先手必敗。記憶化搜尋優化一下複雜度。
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,k; string s; int dp[355][355][355]; int DP(int L,int r,int up1,int up2) { if(dp[L][r][up1] != -1) return dp[L][r][up1]; if(up1 == 0) return 0; else if(up2 == 0) return 1; int f1 = 0,f2 = 0; if(up1 == 1 && s[L - 1] == 'C') f1 = 1; else f1 = DP(L + 1,r,up2,up1 - (s[L - 1] == 'C')); if(up1 == 1 && s[r - 1] == 'C') f2 = 1; else f2 = DP(L,r - 1,up2,up1 - (s[r - 1] == 'C')); if(f1 && f2) return dp[L][r][up1] = 0; else return dp[L][r][up1] = 1; } void solve() { memset(dp,-1,sizeof(dp)); cin >> n >> k >> s; printf("%s\n",DP(1,n,k,k) ? "Yes" : "No"); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; } /* 4 2 CCCP 3 1 CPC */View Code
E:首先有個很顯然的性質,如果起點在i列,走到j列所需要的向左走的步數為x,那麼需要的向右走的步數就是(j - i) + x.
考慮到這一點,我們bfs圖,然後對差值做一個dp的鬆弛即可。
dp[i][j]表示從起點到j所需要的走的最小的向左的步數
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,r,c,x,y; string s[505]; int dp[505][505];//x - y int d[4][2] = {1,0,-1,0,0,1,0,-1}; struct Node{ int x,y; }; void bfs() { queue<Node> Q; Q.push(Node{r - 1,c - 1}); dp[r - 1][c - 1] = 0; while(!Q.empty()) { Node q = Q.front(); Q.pop(); rep(i,0,3) { int px = q.x + d[i][0]; int py = q.y + d[i][1]; if(px >= 0 && px < n && py >= 0 && py < m) { if(s[px][py] == '*') continue; int tmp = dp[q.x][q.y]; if(i == 3) tmp++; //printf("%d %d %d\n",px,py,tmp); if(dp[px][py] > tmp) { dp[px][py] = tmp; Q.push(Node{px,py}); } } } } } void solve() { scanf("%d %d",&n,&m); scanf("%d %d",&r,&c); scanf("%d %d",&x,&y); rep(i,0,n - 1) cin >> s[i]; memset(dp,0x3f3f3f,sizeof(dp)); bfs(); int ans = 0; rep(i,0,n - 1) { rep(j,0,m - 1) { int ri = (j - (c - 1)) + dp[i][j]; if(dp[i][j] <= x && ri <= y) ans++; } } printf("%d\n",ans); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; }View Code
F:《大鍋題》
刪去一個點就把相鄰點的度都減掉1,然後判斷下是否有相鄰點可以加入佇列。
本質上就是一個拓撲排序的做法。(標記少了一些導致一直wa)
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,x,y; vector<int> G[N]; bool vis[N]; int add[N],in[N]; void solve() { scanf("%d %d %d %d",&n,&m,&x,&y); while(m--) { int a,b;scanf("%d %d",&a,&b); G[a].push_back(b); G[b].push_back(a); in[a]++; in[b]++; } queue<int> Q; Q.push(y); vis[y] = 1; while(!Q.empty()) { int u = Q.front(); Q.pop(); for(auto v : G[u]) { if(vis[v]) continue; add[v]++; if(add[v] * 2 >= in[v]) { vis[v] = 1; Q.push(v); } } } if(vis[x]) printf("leave\n"); else printf("stay\n"); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; }View Code
G:轉壓dp,可能不是很好理解。
對於當前集合S,我們選一個最小的湊成S的只刪掉一個點的子集合v去更新它的dp值。
如果這個集合當前不是平衡的,那麼很顯然最少需要一個點再連邊進來,所以dp值 + 1。
假定最終狀態的邊集是E,那麼我們狀壓的過程實際上是先連線了E中的部分邊,然後不斷加入E中的邊進來。
所以正確性可證
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,val[N]; int dp[1 << 20]; void solve() { cin >> n >> m; while(m--) { int a,b,x;cin >> a >> b >> x; val[a] -= x; val[b] += x; } memset(dp,0x3f3f3f,sizeof(dp)); dp[0] = 0; rep(i,1,(1 << n) - 1) { int sum = 0; rep(j,0,n - 1) { if((i >> j) & 1) { sum += val[j]; dp[i] = min(dp[i],dp[i ^ (1 << j)]); } } if(sum != 0) dp[i]++; // printf("dp[%d] is %d\n",i,dp[i]); } printf("%d\n",dp[(1 << n) - 1]); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; }View Code
H:每次選取一個序列中的最小值看是插入到左邊的遞增序列尾部還是右邊的,取距離兩端中最近的一端。
實際上我們並不需要做移動的操作,對於已經插入遞增序列的值,我們打個標記即可。然後x距離兩段的距離其實就是
[1,x - 1]和[x + 1,n]中沒被刪去點的數量,樹狀陣列做一個區間查詢即可.
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 3e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int n,a[N],sum[N]; vector<int> vec[N]; int lowbit(int x) {return x & (-x);} void add(int x) { for(int i = x;i <= n;i += lowbit(i)) sum[i]++; } int query(int x) { int ans = 0; for(int i = x;i;i -= lowbit(i)) ans += sum[i]; return ans; } void solve() { scanf("%d",&n); rep(i,1,n) { scanf("%d",&a[i]); vec[a[i]].push_back(i); } LL ans = 0; rep(i,1,n) { if(vec[i].size() == 0) continue; int L = 0,r = vec[i].size() - 1; while(L <= r) { int lpos = vec[i][L],rpos = vec[i][r]; int le = lpos - 1 - query(lpos); int ri = (n - rpos) - (query(n) - query(rpos)); // printf("%d %d\n",le,ri); if(L == r) { ans += min(le,ri); add(lpos); break; } else { if(le <= ri) { ans += le; add(lpos); L++; } else { ans += ri; add(rpos); r--; } } } } printf("%lld\n",ans); } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} //system("pause"); return 0; }View Code
I:維護一下當前的兩段,然後每次從兩端開始向裡面不斷增加長度,直到這兩段是一樣的。
對於兩段的判斷字串hash預處理一下即可。
#include<bits/stdc++.h> using namespace std; #define sb cout << "sb" << endl; using ll = long long; ll Mod=1e9+7; ll qpow(ll a,ll b) { ll r=1; while(b) { if(b&1)r=r*a%Mod; a=a*a%Mod; b>>=1; } return r; } const int N=1e6+10; char s[N]; ll has1[N],base=2233; ll p[N]; ll query(int l,int r) { return (has1[r]-has1[l-1]*p[r-l+1]%Mod+Mod)%Mod; } int main() { cin >> s+1; p[0]=1; int n=strlen(s+1); for(int i=1;i<=n;++i) { p[i]=p[i-1]*base%Mod; has1[i]=has1[i-1]*base+s[i]; has1[i]%=Mod; } ll sum=0; int l=1,r=n; while(l<r) { int len=0; while(l+len<r-len) { if(query(l,l+len)==query(r-len,r)) { l=l+len+1; r=r-len-1; sum+=2; len=0; break; } len++; } if(l+len>=r-len) break; } if(l<=r)sum++; cout << sum << endl; }View Code
J:可以貪心證明一個圖的每一個連通塊只能有n-1個邊不被塗色
#include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 1e6 + 5; const LL Mod = 998244353; #define rep(at,am,as) for(int at = am;at <= as;++at) #define INF 1e9 #define dbg(ax) cout << "now this num is " << ax << endl; int a[100050],cnt,b,c,d,e,f,g; int find(int g){return a[g]==g?g:a[g]=find(a[g]);} void solve() { cin>>b>>c; for(int i=1;i<=b;i++){ a[i]=i; } for(int i=1;i<=c;i++){ scanf("%d%d",&d,&e); f=find(d); g=find(e); if(f!=g){ a[max(f,g)]=min(f,g); } } for(int i=1;i<=b;i++) if(find(i)!=i) cnt++; cout<<c-cnt<<endl; } int main() { // int _; // for(scanf("%d",&_);_;_--) { solve(); //} // system("pause"); return 0; }View Code