A(签到)
B(贪心)
显然 当最大值的数目为奇数的时候 只有最大值可以留到最后 当最大值的数目为偶数的时候 显然除了最大值本身 任何数字都可以留到最后
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
pair<int,int>a[N];
int b[N];
int n;
void solve(){
cin>>n;
int maxn=0;
int cnt=0;
for(int i=1;i<=n;i++){
cin>>a[i].first;
b[i]=a[i].first;
maxn=max(maxn,a[i].first);
a[i].second=i;
}
for(int i=1;i<=n;i++){
if(a[i].first==maxn)cnt++;
}
if(cnt%2==1){
for(int i=1;i<=n;i++){
if(a[i].first==maxn)cout<<1;
else cout<<0;
}
cout<<'\n';
}else{
for(int i=1;i<=n;i++){
if(a[i].first==maxn)cout<<0;
else cout<<1;
}
cout<<'\n';
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;cin>>t;
while(t--)solve();
return 0;
}
C()
D()
E(构造)
给定一个整数n,要求构造一个n×n的01矩阵同时满足下列条件: 1. 每一行数字的和构成{0,1,2,3,··· ,n−1} 的数字集合。 2. 每一列数字的和构成{0,1,2,3,··· ,n−1} 的数字集合。 3. 数字连通块恰好有n个。 数据范围: 1≤n≤1000
题解:例如: n=6
000000
011111
010000
010111
010100
010101
考虑上图的矩阵,行从0∼n-1,列从0∼n-1,对于(i,j)的答案为 min(i,j) AND 1。 AND: 位运算中的按位与。
cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int n;
cin>>n;
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
int num=min(i,j)&1;
cout<<num;
}
cout<<'\n';
}
return 0;
}
F(位运算 构造)
题目描述 给定一个整数n,构造两个不相等的整数x,y 满足gcd(x,y)=n 且 x ⊕y 最小。 数据范围: 1≤T ≤10^4,1≤n≤10^6
题解:gcd(x,y)=n 我们可以得出x ,y 是n的倍数 并且若x = an y=bn a 与 b互质
异或运算同为1减 不同则加 那么一定有|x −y|≤x ⊕y, x-y的最小值也就是(a-b)n 相邻两个数字一般互质 那么这个值就可以取到1 我们要构造两个数字 使得他俩异或的值等于相减的值也就是n 那么假设n二进制有k位 我们就可以取n<<k 和 (n<<k ) + n 这两个数字 高位直接减去 低位相加 这样结果就是n
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
#define int long long
void solve(){
int n;cin>>n;
int k=1;
while(k<=n){
k<<=1;
}
int x=n*k;
int y=n*(k+1);
cout<<x<<" "<<y<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;cin>>t;
while(t--)solve();
return 0;
}
H(贡献法)
定义一个数组的权值为其所有前缀中不同数字的个数,求所有子数组的 权值之和。
'题解 : 我们可以考虑贡献法 考虑一个数字能给多少个子数组贡献答案以及贡献的大小 当计算a[i]的时候一定包含i本身这个位置 那么我们要考虑 上一个a[i]的位置 j 那么左端点的选择方法有i-j个 右端点的选择方法就有n-i+1 种 那么也就是说 我们可以找到(i-j)*(n-i+1)个子数组 这些子数组又会有各自的前缀 只有覆盖i的前缀才能产生贡献 当左端点固定的时候 右端点产生的n-i+1个子数组 的有效前缀(覆盖位置i)分别有1 2 3 4 5 6 7 .。。(n-i+1) 个 那么也就是总共 (n−i+1)×(n−i+1+1)/2 个前缀 再乘上左端点的个数即可
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
#define int long long
int a[N];
int n;
void solve(){
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
unordered_map<int,int>p; //维护这个数字上一次出现的位置
int ans=0;
for(int i=1;i<=n;i++){
int num=a[i];
int pr=0;
if(p.find(a[i])!=p.end()){
pr=p[num];
}
int numl=i-pr;
int numr=n-i+1;
ans+=1LL*numl*numr*(numr+1)/2;
p[num]=i;
}
cout<<ans<<'\n';
}
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int t;cin>>t;
while(t--)solve();
return 0;
}
I()
大致题意: 一个01矩阵 判断从每一个位置出发 是否都有一条路径 使得路径上所有字符构成的字符串是一个回文串
题解: 我们可以先找到一个终点 和起点相同 若没有显然不成立 找到终点后 我们可以向四周扩展 如果二者有相同的相邻字符 就可以扩展 如果没有 说明这个终点的四周的字符和终点本身相同 那么我们可以不断移动终点 使得二者有相同的相邻字符 不断找下去 就可以发现 只要有两个相同的点 那么最终一定会有一个回文串 只需要判断 0 和1 的数目就可以了
代码不再演示
J(图论、BFS、最短路)
要算出每个点前往更高等级的点的最短路 这里的等级 就是每个点的度数 最朴素的想法 我们可以对每个点进行bfs 这样一定可以解决问题 但是时间复杂度过高 o(nm);
想要优化 我们可以从高等级开始向低等级bfs 因为如果高等级bfs完后 当前等级仍然没有答案 那么他就一定不会有答案了 但是如果从低等级遍历 会导致重复 时间复杂度高
按照这种思路跑一边bfs即可
代码实现如下
cpp
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m;
int deg[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
vector<int>e[N];
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
deg[u]++;deg[v]++;
}
map<int,deque<int>>mp;
for(int i=1;i<=n;i++){
mp[deg[i]].push_back(i);
}
vector<int>dis(n+1,1e9),ans(n+1);
for(auto it=mp.rbegin();it!=mp.rend();it++){
auto x=it->first;
auto &q=it->second;
for(auto &i :q){
if(dis[i]>1e8)dis[i]=-1;
ans[i]=dis[i];
dis[i]=0;
}
while(q.size()){
auto u=q.front();
q.pop_front();
for(auto v:e[u]){
if(deg[v]>=x)continue;
if(dis[v]>dis[u]+1){
dis[v]=dis[u]+1;
q.push_back(v);
}
}
}
}
for(int i=1;i<=n;i++){
cout<<ans[i]<<' ';
}
cout<<'\n';
return 0;
}