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; int n; 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; 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[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; }
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 */
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; }
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; }
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; }
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; }
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; }
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; }