A
思路:
看到两两联通就容易想到最小生成树,然后要黑边多,白边少,那只需要最小生成树改一下排序条件就行了。
代码:
#include<iostream> #include<vector> #include<algorithm> using namespace std; const int maxn = 2e7 + 10; struct edge{ int u,v,w; }e[maxn]; bool cmp(edge a,edge b){ return a.w < b.w; } int f[maxn]; int find(int x){ if(x != f[x]){ return f[x] = find(f[x]); } return f[x]; } void solved(){ int n,m;cin>>n>>m; for(int i = 1; i <= m; i++){ cin >> e[i].u >> e[i].v >> e[i].w; } for(int i = 1; i <= n; i++)f[i] = i; sort(e + 1,e + 1 + m,cmp); int ans = 0; int res = 0; for(int i = 1; i <= m; i++){ int fu = find(e[i].u); int fv = find(e[i].v); if(fu != fv){ ans++; f[fu] = fv; if(e[i].w == 1)res++; if(ans == n - 1)break; } } if(ans != n - 1)cout << "-1" << endl; else cout << res << endl; } int main(){ solved(); return 0; }
C
思路:
要从1最快靠近n的方法,在满足题目约束下,最快的方法是3 + 1 + 3 + 1 + 。。。 + 3 + 1这样,3 + 1 = 4,那么答案就是(n / 4) * 2,然后把余数加进来n % 4,如果余数为3,的话直接跳三步,即操作次数为1.
那么答案就是2 * (x / 4) + ((x % 4 == 3 ? 1 : (x % 4)))
代码:
#include<iostream> #include<vector> #include<algorithm> using namespace std; typedef long long int ll; void solved(ll x){ cout << 2 * (x / 4) + ((x % 4 == 3 ? 1 : (x % 4))) << endl; } int main(){ ll x;cin>>x; solved(x); return 0; }
D
思路:
考虑每个数是否是是素数。
然后求一个素数前缀和。
容易发现答案就是f[n] + 1。
因为:最大的k,就是由k - 1个素数+一个合数组成。
代码:
#include<iostream> #include<vector> #include<algorithm> using namespace std; const int maxn = 2e5 + 10; int f[maxn]; bool check(int x){ for(int i = 2; i * i <= x; i++){ if(x % i == 0)return false; } return true; } int main(){ for(int i = 1; i <= 1e5; i++){ if(check(i)){ f[i] = f[i - 1] + 1; }else{ f[i] = f[i - 1]; } } int n;cin>>n; if(n <= 3){ cout <<"-1" << endl; return 0; } cout << f[n] + 1 << endl; return 0; }
E
思路:
这个没啥好多了,模拟就行了,注意去掉前导0,和结果为0的情况。
代码:
#include<iostream> #include<vector> #include<cstring> #include<algorithm> using namespace std; /* void solved(){ int a,b;cin>>a>>b; vector<int>ans; while(a || b){ int x = a % 10; int y = b % 10; a /= 10;b /= 10; ans.push_back((x + y) % 10); } reverse(ans.begin(),ans.end()); int i = 0; while(i < ans.size() && ans[i] == 0)i++; if(i == ans.size()){ cout << '0' << endl;return ; } for(; i < ans.size(); i++){ cout << ans[i]; } } */ const int maxn = 2e5 + 10; char a[maxn],b[maxn]; void solved(){ a[0] = '0';b[0] = '0'; scanf("%s",a + 1); scanf("%s",b + 1); int len1 = strlen(a + 1); int len2 = strlen(b + 1); vector<int>ans; for(int i = len1,j = len2; true ;){ ans.push_back(((a[i] - '0') + (b[j] - '0')) % 10); if(i == j && i == 0)break; i--;j--; i = max(0,i);j = max(0,j); } reverse(ans.begin(),ans.end()); int i = 0; while(i < ans.size() && ans[i] == 0)i++; if(i == ans.size()){ cout << '0' << endl;return ; } for(; i < ans.size(); i++){ cout << ans[i]; } } int main(){ solved(); return 0; }
H
思路:
容易发现第k小之后的元素没啥用了,我们只需要维护前k个元素即可。
比赛的时候我想的是map维护了,复杂度没问题,就是细节太多了不好写,写了一半还是放弃了。
代码:
#include<iostream> #include<algorithm> #include<vector> #include<string> #include<map> using namespace std; void solved(){ int n,m,k;cin>>n>>m>>k; map<int,int>mp; for(int i = 1; i <= n; i++){ int x;cin>>x; mp[x]++; } map<int,int>::iterator it = mp.begin(); for(k--;k - it->second >= 0 && it != mp.end(); it++){ k -= it->second; } int cnt = k; while(m--){ int ins;cin>>ins; if(ins == 2){ cout << it->first << endl; }else{ int x;cin>>x; mp[x]++; if(x < it->first){ if(cnt == 0){ it--;cnt = it->second; }else{ cnt--; if(cnt == 0){it--;cnt = it->second;} } } } } } int main(){ solved(); return 0; }
用优先队列会比map更好维护,我们只需要搞一个大根堆,先让堆里面的元素个数为k,然后插一个比第k大还小的元素后,pop一个元素,如果比第k大更大的话不需要考虑。
代码:
#include<iostream> #include<vector> #include<algorithm> #include<string> #include<queue> using namespace std; void solved(){ priority_queue<int,vector<int>,less<int> >st; int n,m,k;cin>>n>>m>>k; for(int i = 1; i <= n; i++){ int x;cin>>x;st.push(x); } while(st.size() > k)st.pop(); while(m--){ int ins;cin>>ins; if(ins == 2){ if(st.size() < k)cout << "-1" << endl; else cout << st.top() << endl; }else{ int x;cin>>x; if(x <= st.top() || st.size() < k){ st.push(x); if(st.size() > k) st.pop(); } } } } int main(){ solved(); return 0; }
待补
G
思路:
这题目很容易让人掉进一个坑,先排序a,然后枚举b,二分a中第一个大于b的数,但是这个数之后不能用了,所以这个二分不了。。。。。
然后我想了一个线段树做法:先离散化,然后枚举b,询问[h(bi)+1,2e5]是否有数,但是。。。。这个数要从权值线段树中删除。。。好像不好做。。。想了蛮久不知道咋处理,,,,最终重新审题。。。。
其实我们不关心b的顺序,应该我们要求的只是一个最优的匹配,即bi由在a中第一个大于bi的数匹配,求匹配的个数即可。
所以我们可以对a,b排序,然后双指针,这题就没了。。
代码:
#include<iostream> #include<algorithm> #include<vector> #include<string> using namespace std; const int maxn = 2e5 + 10; typedef long long int ll; ll a[maxn],b[maxn]; void solved(){ int n,m;cin>>n>>m; for(int i = 1; i <= n; i++)cin>>a[i]; for(int i = 1; i <= m; i++)cin>>b[i]; sort(a + 1,a + 1 + n); sort(b + 1,b + 1 + m); int ans = 0; for(int i = 1,j = 1; j <= m;i++,j++){ while(i <= n && a[i] <= b[j])i++; if(i == n + 1)break; ans++; } cout << ans << endl; } int main(){ solved(); return 0; }
J
思路:
场下10分钟给切了,场上瞄了一眼没思路就滚了,害。。。还是切题太慢了。。。
这个题其实是一个很显然的dp。
对于每个数i我们有两种选择,拿或者不拿,如果当前这个数拿的话,那么前面i-1个数不能拿,也就是i-2这个数你可以选择拿或者不拿。
如果当前这个数不拿的话,前面这个数i - 1可以选择拿或者不拿。
那么总结下来就是:
if(mp[i]){ dp[i][0] = max(dp[i - 1][1],dp[i - 1][0]); dp[i][1] = max(dp[i - 2][0],dp[i - 2][1]) + i * mp[i]; }else{ dp[i][0] = dp[i - 1][0]; dp[i][1] = dp[i - 1][1]; }
mp[i]:表示当前这个数i出现的次数,只有这个数出现过我们还有选择拿的权力。
代码:
#include<iostream> #include<vector> #include<algorithm> #include<string> #include<queue> #include<map> using namespace std; const int maxn = 2e5 + 10; typedef long long int ll; int a[maxn]; ll dp[maxn][2]; void solved(){ int n;cin>>n; map<ll,ll>mp; for(int i = 1; i <= n; i++)cin>>a[i],mp[a[i]]++; ll ans = 0; for(int i = 0; i <= 2e5; i++){ if(mp[i]){ dp[i][0] = max(dp[i - 1][1],dp[i - 1][0]); dp[i][1] = max(dp[i - 2][0],dp[i - 2][1]) + i * mp[i]; }else{ dp[i][0] = dp[i - 1][0]; dp[i][1] = dp[i - 1][1]; } ans = max(ans,max(dp[i][0],dp[i][1])); } cout << ans << endl; } int main(){ solved(); return 0; }
B
I
思路:
注意到n很小,那么我们可以O(n^2)求出所有子区间异或和的值,和长度。
然后我们按照异或和的值排序。
那么对于每一个询问,我们二分lower_bound第一个大于等于x的值的位置。
那么[index,N]都是满足要求的,我们需要在这个区间找一个最小的长度那么就是区间最小值问题,线段树维护一下即可。
代码:
#include<iostream> #include<algorithm> #include<vector> #include<string> using namespace std; const int maxn = 5e7 + 10; typedef long long int ll; int m[maxn << 2],c[maxn]; #define lson (rt * 2) #define rson (rt * 2 + 1) #define mid (l + r) / 2 ll b[maxn],a[maxn]; int tot; void build(int l,int r,int rt){ if(l == r){ m[rt] = c[++tot]; return ; } build(l,mid,lson); build(mid + 1,r,rson); m[rt] = min(m[lson],m[rson]); } int query(int ql,int qr,int l,int r,int rt){ if(l >= ql && r <= qr)return m[rt]; int ans = 1e9; if(ql <= mid)ans = min(ans,query(ql,qr,l,mid,lson)); if(qr > mid)ans = min(ans,query(ql,qr,mid + 1,r,rson)); return ans; } bool cmp(pair<ll,int>a,pair<ll,int>b){ return a.first < b.first; } void solved(){ int n,m;scanf("%d%d",&n,&m); for(int i = 1; i <= n; i++)scanf("%lld",&a[i]); vector<pair<ll,int> >ve; for(int i = 1; i <= n; i++){ ll x = a[i]; ve.push_back({x,1}); for(int j = i + 1; j <= n; j++){ x ^= a[j]; ve.push_back({x,j - i + 1}); } } int cnt = 0; sort(ve.begin(),ve.end(),cmp); int N = ve.size(); for(int i = 0; i < ve.size(); i++){ b[++cnt] = ve[i].first; c[cnt] = ve[i].second; } build(1,N,1); b[++cnt] = 1e9 + 7; while(m--){ ll x;scanf("%lld",&x); int index = lower_bound(b + 1,b + 1 + cnt,x) - b; if(index == N + 1)puts("-1"); else printf("%d\n",query(index,N,1,N,1)); } } int main(){ solved(); return 0; }
思路:线段树。
考虑维护[l,r]最大值的时候顺便维护一个计数cnt,容易得到一个简单的pushdown逻辑。
void pushdown(int rt){ if(m[lson] == m[rson]){ m[rt] = m[lson]; cnt[rt] = cnt[lson] + cnt[rson]; }else if(m[lson] > m[rson]){ m[rt] = m[lson]; cnt[rt] = cnt[lson]; }else{ m[rt] = m[rson]; cnt[rt] = cnt[rson]; } }
然后正常单点更新,区间查询就好了。
代码:
#include<iostream> #include<vector> #include<string> #include<map> using namespace std; const int maxn = 2e5 + 10; typedef long long int ll; #define lson (rt * 2) #define rson (rt * 2 + 1) #define mid (l + r) / 2 ll m[maxn << 2]; int cnt[maxn << 2]; void pushdown(int rt){ if(m[lson] == m[rson]){ m[rt] = m[lson]; cnt[rt] = cnt[lson] + cnt[rson]; }else if(m[lson] > m[rson]){ m[rt] = m[lson]; cnt[rt] = cnt[lson]; }else{ m[rt] = m[rson]; cnt[rt] = cnt[rson]; } } void build(int rt,int l,int r){ if(l == r){ scanf("%lld",&m[rt]); cnt[rt] = 1; return ; } build(lson,l,mid); build(rson,mid + 1,r); pushdown(rt); } void query(int rt,int ql,int qr,int l,int r,ll &x,int &y){ if(l >= ql && r <= qr) { if(m[rt] > x){ x = m[rt];y = 0; } if(m[rt] == x)y += cnt[rt]; return ; } if(ql <= mid)query(lson,ql,qr,l,mid,x,y); if(qr > mid)query(rson,ql,qr,mid + 1,r,x,y); } void update(int rt,int l,int r,int pos,int v){ if(l == r){ m[rt] = v; return ; } if(pos <= mid)update(lson,l,mid,pos,v); else update(rson,mid + 1,r,pos,v); pushdown(rt); } void solved(){ int n,m;scanf("%d%d",&n,&m); build(1,1,n); while(m--){ string s;cin>>s; if(s[0] == 'A'){ int l,r;cin>>l>>r; ll x = -1; int y = 0; query(1,l,r,1,n,x,y); printf("%lld %d\n",x,y); }else{ int x,y;cin>>x>>y; update(1,1,n,x,y); } } } int main(){ solved(); return 0; }