其他分享
首页 > 其他分享> > 2022年TZC集训队省赛选拔赛1题解

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而言,分别对应某个权值

所有枚举每个编号即可

#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})

最后答案即为区间的最大值

#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(***)背包/搜索

所以我们可以将差值当作物品大小,好感度之和当作物品价值,通过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(***)前缀和

某个点对应两条直线,对应这两条直线我们可以写出直线方程(相似做法在八皇后问题中出现)

因此我们可以将两条直线压缩成一个点存入两个数组中(只要判断直线存不存在即可)
因为斜率相同的直线之间时没有交点
所以需要考虑两条直线的交点
总数即为 数组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