2022年TZC集训队省赛选拔赛1题解
作者:互联网
A(*)枚举
题意是找到
- 相邻差值最大的一对数
- 如果差值一样,则选择高度最高的那个
- 如果高度也一样,则选择编号最大的数
- 如果差值一样,则选择高度最高的那个
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 110;
int w[N],n;
int res=2;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d",&w[i]);
for(int i=2;i<=n;i++){
if(abs(w[i]-w[i-1])>abs(w[res]-w[res-1])){
res=i;
}
else if(abs(w[i]-w[i-1])==abs(w[res]-w[res-1])){
if(max(w[i],w[i-1])>=max(w[res],w[res-1]))res=i;
}
}
printf("%d %d\n",res-1,res);
return 0;
}
C(*)结构体排序
对每个字符串进行平均值排序,为了防止精度问题,将 / 换成 *
#include<iostream>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
map<string,int>mp;
int idx=0;
struct aa{
string s;
int sum,cnt;
bool operator <(const aa &c)const{
return (ll)sum*c.cnt<(ll)c.sum*cnt;
}
}w[510];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
string s;
cin>>s;
if(!mp.count(s)){
++idx;mp[s]=idx;
w[idx]={s,i,1};
}
else{
int c=mp[s];
w[c].sum+=i;
w[c].cnt++;
}
}
sort(w+1,w+1+idx);
for(int i=1;i<=idx;i++)cout<<w[i].s<<endl;
return 0;
}
H(**)树的前序遍历和后序遍历
将每个遍历后的编号存入数组中
对于某个编号id而言,分别对应某个权值
- 当 c[idx]为0时 -> x - y 不变
- 当 c[idx]为1时
- 前序遍历位置的编号 > 后序遍历位置的编号 -> x - y 增加
- 前序遍历位置的编号 < 后序遍历位置的编号 -> x - y 减小
所有枚举每个编号即可
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 1e5+5,mod=998244353;
vector<int>da,db;
ll d[N];
int s1[N],s2[N];
struct aa{
int l,r;
}tr[N];
void dfs_front(int u){
if(!u)return ;
da.push_back(u);
dfs_front(tr[u].l);
dfs_front(tr[u].r);
}
void dfs_back(int u){
if(!u)return ;
dfs_back(tr[u].l);
dfs_back(tr[u].r);
db.push_back(u);
}
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
tr[i]={a,b};
}
dfs_front(1);
dfs_back(1);
d[0]=1;
for(int i=1;i<=n;i++)d[i]=d[i-1]*2%mod;
for(int i=0;i<da.size();i++)s1[da[i]]=i+1;
for(int i=0;i<db.size();i++)s2[db[i]]=i+1;
ll res=0;
for(int i=1;i<=n;i++){
if(s1[i]<s2[i]){
ll c=(d[n-s1[i]]-d[n-s2[i]])%mod+mod;
res=(res+c)%mod;
}
}
printf("%lld\n",res);
return 0;
}
B(**)暴力
枚举所有回文串,判断这个数是不是质数,存入数组中
排序后通过二分得到比这个数小的回文素数有哪些
即为 get( R ) - get( L-1 )
#include<algorithm>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
typedef long long ll;
vector<ll>vec{2,3,5,7};
int find(ll x){
return lower_bound(vec.begin(),vec.end(),x)-vec.begin();
}
bool check(ll x){
if(x<=1)return false;
for(ll i=2;i<=x/i;i++){
if(x%i==0)return false;
}
return true;
}
ll change(ll x){
ll res=0;
while(x){
res=res*10+x%10;
x/=10;
}
return res;
}
int get(ll x){
int c=find(x);
if(vec[c]==x)c++;
return c;
}
int main(){
ll l,r;
ll f=10;
//cout<<change(1411)<<endl;
for(int i=1;i<1e4;i++){
ll a=i,b= change(i);
if(i==10||i==100||i==1000||i==10000)f*=10;
if(check(a*f+b))vec.push_back(a*f+b);
for(int j=0;j<10;j++){
if(check((a*10+j)*f+b))vec.push_back((a*10+j)*f+b);
//cout<<(a*10+j)*f+b<<endl;
}
}
sort(vec.begin(),vec.end());
scanf("%lld%lld",&l,&r);
printf("%d\n",get(r)-get(l-1));
return 0;
}
I(**)最短路问题
对商店,学校,家两两连边,边的权值为两点的曼哈顿距离
套个最短路模板即可
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 110,INF=0x3f3f3f3f;
int d[N][N];
int f[N];
bool st[N];
int n,m,S,F;
int x[N],y[N];
int dijkstra(){
memset(f,0x3f,sizeof(f));
memset(st,0,sizeof(st));
f[0]=0;
queue<int>que;que.push(0);
while(1){
int u=-1;
for(int i=0;i<=S+1;i++){
if(!st[i]&&(u==-1||f[u]>f[i]))u=i;
}
if(u==-1)return f[S+1];
st[u]=true;
for(int i=0;i<=S+1;i++){
f[i]=min(f[i],f[u]+d[u][i]);
}
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&F,&S);
for(int i=1;i<=S;i++){
scanf("%d%d",&x[i],&y[i]);
}
x[0]=y[0]=1;
x[S+1]=n,y[S+1]=m;
for(int i=0;i<=S+1;i++){
for(int j=0;j<=S+1;j++){
int c=abs(x[i]-x[j])+abs(y[i]-y[j]);
if(c<=F)d[i][j]=c;
else d[i][j]=INF;
}
}
printf("%d\n",dijkstra());
return 0;
}
J(***)线段树求区间最大值
对于某个数字x而言,它可以从 y(abs{x-y}>=k)处转移过来
因此就可以将数组排序后离散化构建线段树
x 从分别从左右两个区间转移过来(c = max{LEFT,RIGHT})
- c + 1 > 原来的数,更新
- c + 1 < 原来的数,不更新
最后答案即为区间的最大值
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
const int N = 3e5+5,INF=1e9;
int n,k;
int w[N];
vector<int>vec({-INF*2-5,INF*2+5});
struct aa{
int l,r;
int max;
}tr[N*4];
int find(int x){
return lower_bound(vec.begin(), vec.end(),x)-vec.begin();
}
void pushup(int u){
tr[u].max=max(tr[u<<1].max,tr[u<<1|1].max);
}
void build(int u,int l,int r){
tr[u]={l,r,0};
if(l==r){
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
}
void modify(int u,int x,int v){
if(tr[u].l==x&&tr[u].r==x){
tr[u].max=max(tr[u].max,v);
return ;
}
int mid=tr[u].l+tr[u].r>>1;
if(x<=mid)modify(u<<1,x,v);
else modify(u<<1|1,x,v);
pushup(u);
}
int query(int u,int l,int r){
if(tr[u].l>=l&&tr[u].r<=r){
return tr[u].max;
}
int mid=tr[u].l+tr[u].r>>1;
int c=0;
if(l<=mid)c= query(u<<1,l,r);
if(r>mid)c=max(c, query(u<<1|1,l,r));
return c;
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
// int a;
scanf("%d",&w[i]);
vec.push_back(w[i]);
}
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
build(1,0,vec.size()-1);
for(int i=1;i<=n;i++){
int L=find(w[i]-k+1)-1,R=find(w[i]+k);
int c=max(query(1,0,L), query(1,R,vec.size()-1));
modify(1,find(w[i]),c+1);
}
printf("%d\n", query(1,0,vec.size()-1));
return 0;
}
G(***)二分 + spfa/贪心(没写)
当更新完某条边后,则两个端点的度数也会发生变化
所以可以用spfa通过变形来实现更新完然后再更新的效果
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 550;
bool dist[N][N];
bool d[N][N];
bool st[N];
int f[N],g[N];
int n,m;
bool spfa(int x){
memcpy(d,dist,sizeof(dist));
memcpy(f,g,sizeof(f));
memset(st,0,sizeof(st));
queue<int>que;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!d[i][j]&&f[i]+f[j]>=x){
d[i][j]=d[j][i]=true;
f[i]++;f[j]++;
if(!st[i])que.push(i),st[i]= true;
if(!st[j])que.push(j),st[j]= true;
}
}
}
while(que.size()){
int i=que.front();que.pop();
st[i]= false;
for(int j=1;j<=n;j++){
if(!d[i][j]&&f[i]+f[j]>=x){
d[i][j]=d[j][i]=true;
f[i]++;f[j]++;
if(!st[i])que.push(i),st[i]= true;
if(!st[j])que.push(j),st[j]= true;
}
}
}
for(int i=1;i<=n;i++){
if(f[i]!=n-1)return false;
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)dist[i][i]= true;
for(int i=1;i<=m;i++){
int a,b;
scanf("%d%d",&a,&b);
g[a]++;g[b]++;
dist[a][b]=dist[b][a]=true;
}
int l=1,r=2*n;
while(l<r){
int mid=l+r+1>>1;
if(spfa(mid))l=mid;
else r=mid-1;
}
printf("%d\n",l);
return 0;
}
D(***)背包/搜索
- 我们可以将好感度相减,则可得到两者最终好感度的差值(最终答案需要保证差值最小)
- 然后将好感动相加,则可得到两则最终好感动的总和(用来判断是否大于s)
所以我们可以将差值当作物品大小,好感度之和当作物品价值,通过n、Ai、Bi算出背包大小
因为好感度差值可能为负数,所以加上一个大数使这个区间全为正数
通过背包即可得到答案
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 12000+15,M = 6005;
int n,m;
int dp[33][N];
bool st[33][N];
int main(){
scanf("%d%d",&n,&m);
memset(dp,-0x3f,sizeof(dp));
dp[0][M]=0;st[0][M]= true;
for(int i=1;i<=n;i++){
int a,b;
scanf("%d%d",&a,&b);
int w=a-b,v=a+b;
for(int j=0;j<N;j++){
dp[i][j]=max(dp[i-1][j],dp[i][j]);
if(!(i==1&&j==M))st[i][j]=st[i][j]||st[i-1][j];
int c=j+w;
if(c>=0&&c<N){
dp[i][c]=max(dp[i][c],dp[i-1][j]+v);
st[i][c]=st[i][c]||st[i-1][j];
}
}
// for(int j=0;j<=10;j++){
// printf("%d %d\n",dp[i][M+j],dp[i][M-j]);
// }
// puts("");
}
for(int i=0;i<=M;i++){
// printf("%d %d\n",dp[n][M+i],dp[n][M-i]);
if(dp[n][M+i]>m&&st[n][M+i]||dp[n][M-i]>m&&st[n][M-i]){
printf("%d\n",i);
return 0;
}
}
printf("%d",-1);
return 0;
}
E(***)前缀和
某个点对应两条直线,对应这两条直线我们可以写出直线方程(相似做法在八皇后问题中出现)
- x + y + c = 0
- x + y = c
- x - y - c = 0
- x - y = c
因此我们可以将两条直线压缩成一个点存入两个数组中(只要判断直线存不存在即可)
因为斜率相同的直线之间时没有交点
所以需要考虑两条直线的交点
总数即为 数组1中点的个数 + 数组2中点的个数 - 相交的点
枚举第二个数组中的直线,可以得到直线与棋盘边界的两个交点
- 通过两个交点我们可以映射成数组一中的两个值,也就是一个区间,查看区间中有多少个交点(前缀和维护即可)
注意因为交点可能不是整数点,所以我们需要隔二做前缀和
例如
2 2
1 1
1 2
答案应为4
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e6+5,M=1e6;
typedef long long ll;
int n,m;
ll res=0;
ll tr[N*2];
bool d1[2*N],d2[2*N];
int get_1(int x){
int d=1+n,ans=n;
return ans-abs(d-x);
}
int get_2(int x){
int d=0,ans=n;
return ans-abs(d-x);
}
int main(){
scanf("%d%d",&n,&m);
while(m--){
int x,y;
scanf("%d%d",&x,&y);
d1[x+y]=true;d2[x-y+M]=true;
}
for(int i=2;i<=n+n;i++){
tr[i]=tr[i-2];
if(d1[i]){
res+= get_1(i);
tr[i]++;
}
}
for(int i=1-n;i<=n-1;i++){
if(!d2[i+M])continue;
if(i<=0){
int L=2-i,R=2*n+i;
res+= get_2(i)-tr[R]+tr[L-2];
}
else{
int L=i+2,R=2*n-i;
res+= get_2(i)-tr[R]+tr[L-2];
}
}
printf("%lld\n",res);
return 0;
}
F(****)树状数组 + vector插入/平衡树
不详细解释,想解的直接看代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<list>
using namespace std;
const int N = 5e5+5;
int tr[N],n,idx;
//每一段有哪些人
vector<int>lis[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)){
tr[i]++;
}
}
int query(int x){
int res=0;
for(int i=x;i;i-= lowbit(i)){
res+=tr[i];
}
return res;
}
int get_back(int x,vector<int>v,int sum){
//前面有多少人 一共有前面sum-1个人
int d= query(v[x]);
return sum-1-d;
}
void pushback(int c,int i){
++idx;
vec[c].push_back(idx);
lis[idx].push_back(i);//这一段里面有哪些人
add(idx);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
int c,a;
scanf("%d%d",&c,&a);
if(vec[c].empty()){
//没地方可插队
pushback(c,i);
}
else{
int l=0,r=vec[c].size()-1;
while(l<r){
int mid=l+r>>1;
if(get_back(mid,vec[c],i)<=a)r=mid;
else l=mid+1;
}
int people= get_back(l,vec[c],i);
if(people<=a){
//有地方可以插队
int p_id=vec[c][l];
int d=max((int)lis[p_id].size()-(a-people),0);
add(p_id);lis[p_id].insert(lis[p_id].begin()+d,i);
}
else{
//没地方可插队
pushback(c,i);
}
}
}
bool flag= false;
for(int i=1;i<=idx;i++){
for(auto c:lis[i]){
if(flag)printf(" ");
printf("%d",c);
flag=true;
}
}
return 0;
}
标签:return,idx,int,题解,st,TZC,2022,res,include 来源: https://www.cnblogs.com/syf2020/p/15973182.html