B:华华对月月的忠诚
思路:因为最后要求的是gcd(a,b),所以我一开始是分类讨论的。
如果a和b都是偶数,那么他们的斐波拉契项都是偶数,对于任意位置的n和n+1,偶数的gcd都是2.
如果a和b都是奇数,或者是奇数与偶数,他们的斐波拉契项有可能有奇数或者偶数,一开始我猜想都是1,事实上大部分都是1,但是随手打了个表就找到了反例,a=3,b=9,然后我就发现事实上这个答案就是gcd(a,b)。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long int ll; void solved(){ ll a,b,n;cin>>a>>b>>n; cout<<__gcd(a,b)<<endl; } int main(){ solved(); return 0; }
D:挖沟
这个题。。。。。裸的最小生成树。。直接上板子就行。
克鲁斯卡尔算法:存边,对边权排序,选择n-1条最小边,并且不构成回路(并查集维护).
好久没写最小生成树了,现场花了几分钟手搓了一下
代码:
#include<bits/stdc++.h> using namespace std; typedef long long int ll; const int maxn = 5e5 + 10; struct edge{ int u,v,w; edge(){} edge(int a,int b,int c):u(a),v(b),w(c){} }a[maxn]; int f[maxn]; bool cmp(edge a,edge b){ return a.w < b.w; } int find(int x){ if(f[x] != x){ return f[x] = find(f[x]); } return x; } void solved(){ int n,m;cin>>n>>m; for(int i = 1; i <= n; i++)f[i] = i; for(int i = 1; i <= m; i++){ int u,v,w;cin>>u>>v>>w; a[i] = {u,v,w}; } sort(a + 1 ,a + 1 + m,cmp); int tot = 0; ll ans = 0; for(int i = 1; i <= m; i++){ int u = a[i].u,v = a[i].v; int fa = find(u); int fb = find(v); if(fa != fb){ f[fa] = fb; tot++; ans += a[i].w; } if(tot == n - 1)break; } cout<<ans<<endl; } int main(){ solved(); return 0; }
C:Game
思路:这题从一次操作可以将集合中一个数字分解为它的任意两个非1的因数入手,首先我们可以考虑特殊的数-素数,只能分解为1*n,所以先手必败,然后再思考普遍的情况。对于一个数,我们可以一直把他拆开,直到不能再拆为止,记录我们拆的次数,如果是奇数说明后手无法继续拆,偶数先手无法拆,输出对应答案即可。
代码:
#include<bits/stdc++.h> using namespace std; bool is_prime(int n){ for(int i = 2; i * i <= n; i++){ if(n % i == 0)return false; }return true; } void solved(){ int n;cin>>n; if(is_prime(n)){cout<<"Nancy"<<endl;return;} queue<int>st;st.push(n); int cnt = 0; while(!st.empty()){ int cur = st.front();st.pop(); for(int i = 2; i * i <= cur; i++){ if(cur % i == 0){ cnt++; st.push(i);st.push(cur / i);break; } } } if(cnt & 1)cout<<"Johnson"<<endl; else cout<<"Nancy"<<endl; } int main(){ solved(); return 0; }
E:签到题
思路:看到题直接就认为是记录每个数出现的次数,然后上线段树。。。。导致浪费好多时间。。还是先应该认真看题啊。。。题目意思是插入n段区间要我们就所有区间的并的个数,删除的区间必须是先前插入过的才行。
区间修改,区间求和,所以我们可以用线段树来维护,线段树维护cnt表示插入删除的次数,len表示当前区间长度。如果cnt为真说明这段区间已经插入过了,它的区间长度就是本身,否则,它的区间长度等于左右儿子的长度之和。如果插入的区间与已有的区间重合怎么处理?对于重合的区间说明之前已经插入过,它的cnt为真所以不用处理,就变成了只对没重合的处理。
代码:
#include <algorithm> #include <iostream> #include <cstring> #include <climits> #include <cstdio> #include <vector> #include <cstdlib> #include <ctime> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> #define fi first #define lc (x<<1) #define se second #define U unsigned #define rc (x<<1|1) #define Re register #define LL long long #define MP std::make_pair #define CLR(i,a) memset(i,a,sizeof(i)) #define FOR(i,a,b) for(Re int i = a;i <= b;++i) #define ROF(i,a,b) for(Re int i = a;i >= b;--i) #define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c) #define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c) #define DEBUG(x) std::cerr << #x << '=' << x << std::endl using namespace std; const int MAXN = 1000000+5; int N,maxL; std::set<std::pair<int,int> > L; const int maxn = 1e6 + 10; struct seg{ int l,r,cnt; int len; seg(){} seg(int a,int b):l(a),r(b){} }tree[maxn << 2]; void build(int rt,int l,int r){ tree[rt] = {l,r}; if(l == r){ tree[rt].len = 0; tree[rt].cnt = 0; return; } int mid = l + r >> 1; build(rt << 1,l, mid); build((rt << 1) + 1,mid + 1,r); tree[rt].cnt = 0; tree[rt].len = 0; } void update(int rt ,int l,int r,int v){ if(tree[rt].l >= l && tree[rt].r <= r){ tree[rt].cnt += v; if(tree[rt].cnt)tree[rt].len = (tree[rt].r - tree[rt].l + 1); else {tree[rt].len = tree[rt << 1].len + tree[rt << 1 | 1].len;} return ; } int mid = tree[rt].l + tree[rt].r >> 1; if(r <= mid) update(rt << 1,l,r,v); else if(l > mid) update((rt << 1) + 1,l,r,v); else update(rt << 1,l,r,v),update((rt << 1) + 1,l,r,v); if(tree[rt].cnt)tree[rt].len = (tree[rt].r - tree[rt].l + 1); else {tree[rt].len = tree[rt << 1].len + tree[rt << 1 | 1].len;} } int main(){ scanf("%d%d",&N,&maxL); build(1,1,maxL); while(N--){ int opt,x,y; scanf("%d%d%d",&opt,&x,&y); if(opt == 1){ if(L.find(MP(x,y)) != L.end()) continue; L.insert(MP(x,y)); update(1,x,y,1); } if(opt == 2){ if(L.find(MP(x,y)) == L.end()) continue; L.erase(MP(x,y)); update(1,x,y,-1); } if(opt == 3){ printf("%d\n",tree[1].len); } } return 0; }