牛客练习赛112

A.

直接把1e6的质数筛出来

然后枚举每一位即可

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 5e6+10;
typedef pair<int, int> PII;
int primes[N],cnt;
bool st[N];
int n;
int a[N];
void count(int n){
    for(int i=2;i<=n;i++){
        if(!st[i]) primes[cnt++]=i,a[i]=1;
        for(int j=0;i<=n/primes[j];j++){
            st[i*primes[j]]=true;
            if(i%primes[j]==0) break;
        }
    }
}
void solve(){
    string s;
    cin>>s;
    int x=0;
    for(int i=0;i<s.size();i++){
        x=x*10+(s[i]-'0');
        if(!a[x]){
            cout<<"NO\n";
            return ;
        }
    }
    cout<<"YES\n";
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;
    cin>>t;
    count(1000010);
    while(t--) solve();
}

B.

有两个结论

就30个二进制位有一个0当前位最终&值就肯定是0
只要你 l到r的与是0,那么[1,i-1]到r也同样是0

直接预处理前面的数每个二进制位存在0的最大的位置

然后以i为右端点,找a[i]二进制为1的位数,因为a[i]为1,那么前面一定要存在一个0
 

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int N = 1e6+10;
typedef pair<int, int> PII;
int a[N];
int n,m;
int cnt[N][31];
void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    long long res=0;
    int now=0;
    for(int i=1;i<=n;i++){
        for(int j=0;j<=30;j++)
        if(((a[i]>>j)&1)==0) cnt[i][j]=i;
        else cnt[i][j]=cnt[i-1][j];
    }
    for(int i=1;i<=n;i++)
    {
        int mx=i;
        for(int j=0;j<=30;j++)
        {
            if(a[i]>>j&1){
                mx=min(mx,cnt[i][j]);
            }
        }
        res+=mx;
    }
    cout<<res;
    
}
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    int t=1;

    while(t--) solve();
}

C.

就是求最长公共子序列即可

公共子序列是不用动的,其他数插入进来即可,也不改变顺序

因为是一个全排列

可以把a离散化

然后最后求b的最长上升子序列即可

比如

A=1 2 3 4 5 6

B=6 3 4 5 12

求b的最长上升子序列

# include <iostream>
# include <vector>
# include <map>
 
using namespace std;
 
const int maxn = 1e6 + 10;
int n;
map<int, int>m;
int B[maxn];
 
int main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
	cin >> n;
	int a;
	for (int i = 1; i <= n; i++) {
		cin >> a;
		m[a] = i;
	}
	int b;
	for (int i = 1; i <= n; i++) {
		cin >> b;

		B[i] = m[b];
	}
	vector<int>C;
	C.push_back(0);
	int len = 0; 
	for (int i = 1; i <= n; i++) {
		if (B[i] > C[len]) {
			C.push_back(B[i]);
			len++;
		}
		else {
			C[lower_bound(C.begin(), C.end(), B[i]) - C.begin()] = B[i];
		}
	}
	cout << n-len << endl;
	return 0;
}

D.

这个题是最短路毕竟是求最短时间0.0

这个题是从开头点(不是出发点)出发,每t秒开一次

为了方便我们存图的时候记录距离开头点和当前点中间相差多少个站(i-1,可能有人疑惑???,解释一下,因为是求开头点到当前点最短路所以当前点还没到,有效距离是不算当前站点的)

然后直接dijkstra,然后计算上一个开头点和当前点的时间和k*t的差即可

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
#define int long long
typedef pair<int, int> PII;
using node=tuple<int,int,int> ;
int n,m,s;
vector<node> g[N];
signed main()
{
    cin.tie(0);cout.tie(0);ios::sync_with_stdio(0);
    cin>>n>>m>>s;
    for(int i=0;i<m;i++){
        int k,t;cin>>k>>t;
        vector<int> c(k);
        for(auto&x:c) cin>>x;
        for(int i=1;i<k;i++){
            g[c[i-1]].emplace_back(c[i],t,i-1);
        }
    }
    vector<int> d(n+1,-1);
    priority_queue<PII,vector<PII>,greater<PII>> q;
    q.push({d[s]=0,s});
    while(q.size()){
        auto p=q.top();
        q.pop();
        int u=p.second;
        if(p.first>d[u]) continue;
        for(auto [v,t,ed]:g[u]){
            int w;
            if(ed>=d[u])
            {
                w=ed+1;
            }
            else
            {
               w=(d[u]-ed+t-1)/t*t+ed+1;
            }
            if(d[v]==-1||d[v]>w)
            {
                q.push({d[v]=w,v});
            }
        }
    }
    for(int i=1;i<=n;i++) cout<<d[i]<<"\n";
}

E.

把绝对值拆开,然后分类讨论即可

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
struct Node {
    LL x, y, id;
} t[5][N];
bool cmp1(Node a, Node b) {
    if (a.x + a.y != b.x + b.y)
        return a.x + a.y < b.x + b.y;

    return a.id < b.id;
}
bool cmp2(Node a, Node b) {
    if (a.x + a.y != b.x + b.y)
        return a.x + a.y > b.x + b.y;

    return a.id < b.id;
}
bool cmp3(Node a, Node b) {
    if (a.x - a.y != b.x - b.y)
        return a.x - a.y < b.x - b.y;

    return a.id < b.id;
}
bool cmp4(Node a, Node b) {
    if (a.x - a.y != b.x - b.y)
        return a.x - a.y > b.x - b.y;

    return a.id < b.id;
}
bool vis[N];
void solve() {
    int n;
    cin >> n;

    for (int i = 1; i <= n; ++i) {
        int x, y;
        cin >> x >> y;

        for (int j = 1; j <= 4; ++j)
            t[j][i] = {x, y, i};
    }

    sort(t[1] + 1, t[1] + 1 + n, cmp1);
    sort(t[2] + 1, t[2] + 1 + n, cmp2);
    sort(t[3] + 1, t[3] + 1 + n, cmp3);
    sort(t[4] + 1, t[4] + 1 + n, cmp4);
    int p[5];

    for (int i = 1; i <= 4; ++i)
        p[i] = 1;

    LL x = 0, y = 0;
    LL ans = 0;

    for (int k = 1; k <= n; ++k) {
        LL dis = -1, re;

        for (int i = 1; i <= 4; ++i) {
            while (p[i] <= n && vis[t[i][p[i]].id]) {
                ++p[i];
            }

            if(i==1){
                LL tmp=x+y-(t[i][p[i]].x+t[i][p[i]].y);
                tmp=abs(tmp);
                if(tmp>dis){
                    dis=tmp;
                    re=i;
                }
                else if(tmp==dis&&t[re][p[re]].id>t[i][p[i]].id){
                    re=i;
                }
            }
            if(i==2){
                LL tmp=-x-y+(t[i][p[i]].x+t[i][p[i]].y);
                tmp=abs(tmp);
                if(tmp>dis){
                    dis=tmp;
                    re=i;
                }
                else if(tmp==dis&&t[re][p[re]].id>t[i][p[i]].id){
                    re=i;
                }
            }
            if(i==3){
                LL tmp=x -y - (t[i][p[i]].x - t[i][p[i]].y);
                tmp=abs(tmp);
                if(tmp>dis){
                    dis=tmp;
                    re=i;
                }
                else if(tmp==dis&&t[re][p[re]].id>t[i][p[i]].id){
                    re=i;
                }
            }
            if(i==4){
                LL tmp=-x + y + (t[i][p[i]].x - t[i][p[i]].y);
                tmp=abs(tmp);
                if(tmp>dis){
                    dis=tmp;
                    re=i;
                }
                else if(tmp==dis&&t[re][p[re]].id>t[i][p[i]].id){
                    re=i;
                }
            }
        }

        ans += dis;
        x = t[re][p[re]].x;
        y = t[re][p[re]].y;
        vis[t[re][p[re]].id] = 1;
    }

    cout << ans << '\n';
}
int main() {
    //  freopen("a.txt","r",stdin);
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;

    //  cin>>t;
    while (t--) {
        solve();
    }

    return 0;
}

F.

换根dp

看范围油费<=50

状态设置为g2以u为根,最低油费为k的油费总和

g1就是以u为根到每个儿子节点路径中最低油费为k的节点个数

f1同理

第一次dfs

如果k<c[u],正常转移到k即可

但如果c[u]>k,那么只能全部转移到c[u],因为这个路径最小的肯定只有u,一定包含根节点

这个时候先考虑不用c[u]这个加油站,先用子树里面最小的加油站

第二次dfs换根

首先要加上父节点子树上面的价值,同时又因为父节点子树包含了自己,但是自己又已经有自己,所以要(f1[u][k] - g1[j][k]) * w[i] * k减去多了的自己总和

最后考虑c[u]父节点这个加油站,把前面大于c[u]且不考虑的加油站全去掉

改用c[u]这个更小的加油站

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10, M = 1e6 + 10;
LL f1[N][51], f2[N][51], g1[N][51], g2[N][51];
int c[N];
int h[N], w[M], e[M], ne[M], idx;
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dfs1(int u, int fa) {
    g1[u][c[u]]++;

    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];

        if (j == fa)
            continue;

        dfs1(j, u);

        for (int k = 1; k <= 50; ++k) {
            if (k < c[u]) {
                g1[u][k] += g1[j][k];
                g2[u][k] += g2[j][k] + g1[j][k] * k * w[i];
            } else {
                g1[u][c[u]] += g1[j][k];
                g2[u][c[u]] += g2[j][k] + g1[j][k] * k * w[i];
            }
        }
    }
}
void dfs2(int u, int fa) {
    for (int k = 1; k <= 50; ++k) {
        f1[u][k] += g1[u][k];
        f2[u][k] += g2[u][k];
    }

    for (int i = h[u]; ~i; i = ne[i]) {
        int j = e[i];

        if (j == fa)
            continue;

        for (int k = 1; k < c[u]; ++k) {
            f1[j][min(c[j], k)] += f1[u][k] - g1[j][k];
            f2[j][min(c[j], k)] += f2[u][k] - (g2[j][k] + g1[j][k] * k * w[i])
                                   + (f1[u][k] - g1[j][k]) * w[i] * k;
        }

        LL s1 = f1[u][c[u]], s2 = f2[u][c[u]];

        for (int k = c[u]; k <= 50; ++k) {
            s2 -= g2[j][k] + k * w[i] * g1[j][k];
            s1 -= g1[j][k];
        }

        f1[j][min(c[j], c[u])] += s1;
        f2[j][min(c[j], c[u])] += s2 + s1 * w[i] * c[u];
        dfs2(j, u);
    }
}
void solve() {
    int n;
    cin >> n;
    memset(h, -1, sizeof h);

    for (int i = 1; i <= n; ++i)
        cin >> c[i];

    for (int i = 1; i <= n - 1; ++i) {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }

    dfs1(1, 0);
    dfs2(1, 0);
    LL ans = 0;

    for (int i = 1; i <= n; ++i) {
        for (int k = 1; k <= 50; ++k) {
            ans += f2[i][k];
        }
    }

    cout << ans << '\n';
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int t = 1;

    //  cin>>t;
    while (t--) {
        solve();
    }

    return 0;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值