《Codeforces Round #738 (Div. 2)》
A:很显然对于每个bit位,只要存在一个0,最终都能通过很多次操作后全部变成0.统计全部为1的位数即可。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int a[105],tag[35]; int main() { int ca;scanf("%d",&ca); while(ca--) { int n;scanf("%d",&n); memset(tag,0,sizeof(tag)); for(int i = 1;i <= n;++i) { scanf("%d",&a[i]); for(int j = 0;j <= 30;++j) { int g = ((a[i] >> j) & 1); if(g == 0) tag[j] = 1; } } int ma = 0; for(int i = 0;i <= 30;++i) { if(tag[i] != 1) ma += (1 << i); } printf("%d\n",ma); } // system("pause"); return 0; }
B:每次都放置与前面不同显然最优,这里比较特殊的是以?开头,这时候要第一个字母和前面的问号不一样放置
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 2e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; char s[105]; char cal(char c) { if(c == 'B') return 'R'; else return 'B'; } int main() { int ca;scanf("%d",&ca); while(ca--) { int n;scanf("%d",&n); cin >> s; char pre = 'A'; int len = 0; if(s[0] == '?') { for(int i = 0;i < n;++i) { if(s[i] != '?') {pre = s[i];break;} ++len; } if(pre == 'A') s[0] = 'B'; else { if(len % 2 == 0) s[0] = pre; else s[0] = cal(pre); } } for(int i = 1;i < n;++i) { if(s[i] != '?') continue; s[i] = cal(s[i - 1]); } cout << s << endl; } // system("pause"); return 0; } /* 30 7 ??BBR?? */
C:一开始直接冲哈密尔顿通路去了。仔细一看这个图其实比较特殊。
然后分析一下就可以发现,如果存在到终点的路径,那么一定可以到。
反之也一定到,因为如果存在到1的路径就可以从终点到1开始。
综上所诉,一定存在一种走法满足。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e4 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int a[N]; vector<int> ans; void PR() { for(int i = 0;i < ans.size();++i) printf("%d%c",ans[i],i == ans.size() - 1 ? '\n' : ' '); } int main() { int ca;scanf("%d",&ca); while(ca--) { int n;scanf("%d",&n); for(int i = 1;i <= n;++i) scanf("%d",&a[i]); ans.clear(); if(n == 1) { if(a[1] == 0) ans.push_back(1),ans.push_back(2); else ans.push_back(2),ans.push_back(1); PR(); } else { if(a[1] == 1) { ans.push_back(n + 1); for(int i = 1;i <= n;++i) ans.push_back(i); PR(); } else if(a[n] == 0) { for(int i = 1;i <= n;++i) ans.push_back(i); ans.push_back(n + 1); PR(); } else { int st = 0; for(int i = 1;i < n;++i) { if(a[i] == 0 && a[i + 1] == 1) { st = i; break; } } if(st != 0) { for(int i = 1;i <= st;++i) ans.push_back(i); ans.push_back(n + 1); for(int i = st + 1;i <= n;++i) ans.push_back(i); PR(); } //else printf("-1\n"); } } } // system("pause"); return 0; }
D1:直接n^2暴力并查集合并判断即可。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e3 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m1,m2,fa[N]; namespace UN1{ int fa[N]; void init() { for(int i = 1;i <= n;++i) fa[i] = i; } int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } void Merge(int x,int y) { x = Find(x),y = Find(y); fa[x] = y; } bool check(int x,int y) { x = Find(x),y = Find(y); return x != y; } } namespace UN2{ int fa[N]; void init() { for(int i = 1;i <= n;++i) fa[i] = i; } int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } void Merge(int x,int y) { x = Find(x),y = Find(y); fa[x] = y; } bool check(int x,int y) { x = Find(x),y = Find(y); return x != y; } } int main() { scanf("%d %d %d",&n,&m1,&m2); UN1::init(); UN2::init(); while(m1--) { int u,v;scanf("%d %d",&u,&v); UN1::Merge(u,v); } while(m2--) { int u,v;scanf("%d %d",&u,&v); UN2::Merge(u,v); } vector<pii> vec; for(int i = 1;i <= n;++i) { for(int j = i + 1;j <= n;++j) { if(UN1::check(i,j) && UN2::check(i,j)) { UN1::Merge(i,j); UN2::Merge(i,j); vec.push_back(pii{i,j}); } } } printf("%d\n",vec.size()); for(auto v : vec) printf("%d %d\n",v.first,v.second); // system("pause"); return 0; }
D2:这里的思路还是比较抽象的感觉。
枚举每个点和1的连接状态。如果对于当前点v,都和1没有连接。
那么就可以加上Edeg{1,v}这条边。
反之,准备两个容器v1,v2。对于没有和1连接的边,先放入容器。
因为这些边可能由于和{i,i为和1同一并查集的某个点}连接而和1间接连接。
现在我们考虑容器中的两个边x,y。
他们肯定满足都不在1的连通块中,且他们在另一个森林中都和1在同一连通块中。(否则两个点就都在1的连通块中)
即可以看成:
{1 and y} ,{x}
{1 and x} ,{y}
那么就可以建立{x,y}这条边。
所以对于容器中的任意两个点都可以建边。
不过要注意的是,可能两个边连之后,容器中某些边都被合并在1的连通块中了。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5 + 5; const int M = 5e5 + 5; const LL Mod = 1e9 + 7; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m1,m2; namespace UN1{ int fa[N]; void init() { for(int i = 1;i <= n;++i) fa[i] = i; } int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } void Merge(int x,int y) { x = Find(x),y = Find(y); fa[x] = y; } bool check(int x,int y) { x = Find(x),y = Find(y); return x != y; } } namespace UN2{ int fa[N]; void init() { for(int i = 1;i <= n;++i) fa[i] = i; } int Find(int x) { return x == fa[x] ? x : fa[x] = Find(fa[x]); } void Merge(int x,int y) { x = Find(x),y = Find(y); if(x < y) swap(x,y); fa[x] = y; } bool check(int x,int y) { x = Find(x),y = Find(y); return x != y; } } vector<pii> vec; vector<int> v1,v2; int main() { scanf("%d %d %d",&n,&m1,&m2); UN1::init(); UN2::init(); while(m1--) { int u,v;scanf("%d %d",&u,&v); UN1::Merge(u,v); } while(m2--) { int u,v;scanf("%d %d",&u,&v); UN2::Merge(u,v); } for(int i = 2;i <= n;++i) { if(UN1::check(1,i) && UN2::check(1,i)) { vec.push_back(pii{1,i}); UN1::Merge(1,i); UN2::Merge(1,i); } else if(UN1::check(1,i)) v1.push_back(i); else if(UN2::check(1,i)) v2.push_back(i); } for(int i = 0,j = 0;i < v1.size() && j < v2.size();) { while(i < v1.size() && (!UN1::check(v1[i],1) && !UN2::check(v1[i],1))) { ++i; } while(j < v2.size() && (!UN2::check(v2[j],1) && !UN1::check(v2[j],1))) { ++j; } if(i >= v1.size() || j >= v2.size()) break; vec.push_back(pii{v1[i],v2[j]}); UN1::Merge(v1[i],v2[j]); UN2::Merge(v2[j],v1[i]); ++i,++j; } printf("%d\n",vec.size()); for(auto v : vec) printf("%d %d\n",v.first,v.second); //system("pause"); return 0; }
E:这题挺不错的
首先有个很直白的三维dp,但是显然不可能这样做。
设dp[j] - 表示a1 + a2 + ... an == j的方案数。
则有:
$ans =\sum_{a1 = L1}^{r1} \sum_{a2 = L2}^{r2} ... \sum_{an = Ln}^{rn} [gcd(a1,a2...an) = 1] * dp[a1,a2....an]$
$ans = \sum_{a1 = L1}^{r1} \sum_{a2 = L2}^{r2} ... \sum_{an = Ln}^{rn} \sum_{d | (a1,a2...an)} \mu (d)* dp[a1,a2....an]$
$ans = \sum_{d = 1}^{m} \mu (d) \sum_{a1 = [\frac{L1}{d}]}^{[\frac{r1}{d}]} \sum_{a2 = [\frac{L2}{d}]}^{[\frac{r2}{d}]} ... \sum_{an = [\frac{Ln}{d}]}^{[\frac{rn}{d}]} dp[a1 * d,a2 * d....an * d]$
到这一步我们可以发现,我们只需要满足a1d + a2d + a3d + ... + and <= m
即$a1 + a2 + a3 + .. an <= [\frac{m}{d}]$
至此,对于每个d,贡献即为$\sum_{i = 1}^{[\frac{m}{d}]}dp[i] * \mu (d)$
考虑一下复杂度优化:对于dp[j]的计算,枚举d,n,对于第三维,我们只需要到m / d,
平摊下来复杂度接近O(N * M)。
一些细节:对于dp[j]的计算,我们可以利用前缀和来优化掉一维。
然后考虑一下我们的上界和下界。
如果我们当前ai ~ [1.5,3.5],那么我们的上界应该是2,3,因为我们1和4应该是取不到的。
所以我们对于下界应该向上取整。
// Author: levil #include<bits/stdc++.h> using namespace std; typedef long long LL; typedef pair<int,int> pii; const int N = 1e5 + 5; const int M = 5e5 + 5; const LL Mod = 998244353; #define pi acos(-1) #define INF 1e16 #define dbg(ax) cout << "now this num is " << ax << endl; int n,m,L[55],r[55],prime[N],tot = 0; LL dp[55][N],sum[55][N],mu[N]; bool vis[N]; void init() { mu[1] = 1; for(int i = 2;i < N;++i) { if(!vis[i]) { prime[++tot] = i; mu[i] = -1; } for(int j = 1;j <= tot && prime[j] * i < N;++j) { vis[i * prime[j]] = 1; if(i % prime[j] == 0) break; else mu[i * prime[j]] = -mu[i]; } } } int main() { init(); scanf("%d %d",&n,&m); for(int i = 1;i <= n;++i) scanf("%d %d",&L[i],&r[i]); LL ans = 0; for(int d = 1;d <= m;++d) { dp[0][0] = 1; for(int i = 1;i <= n;++i) { sum[i - 1][0] = dp[i - 1][0]; for(int j = 1;j <= m / d;++j) { sum[i - 1][j] = (sum[i - 1][j - 1] + dp[i - 1][j]) % Mod; } int le = (L[i] + d - 1) / d,ri = r[i] / d;//le向上取整,ri向下取整 for(int j = le;j <= m / d;++j) { if(j - ri - 1 < 0) dp[i][j] = sum[i - 1][j - le]; else dp[i][j] = sum[i - 1][j - le] - sum[i - 1][j - ri - 1]; //dp[i][j] = sum[i - 1][j - le] - sum[i - 1][max(0,j - ri)];0也应该算在贡献里 dp[i][j] = (dp[i][j] + Mod) % Mod; } } LL cnt = 0; for(int j = 1;j <= m / d;++j) cnt = (cnt + dp[n][j]) % Mod; ans = (ans + cnt * (mu[d]) % Mod) % Mod; ans = (ans + Mod) % Mod; } printf("%lld\n",ans); system("pause"); return 0; }