WUST周赛一

A.WUST周赛一 

分析:满足条件的i,j中,i和j必有一个是2

-------->首先,i,j如果都不是2,同时他们又是质数,那么只能是奇数,奇数+奇数=偶数,所以肯定不成立,所以如果满足条件,那么i,j必有一个是2

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], primes[N], cnt;
bool st[N];
void pre() {
    for (int i = 2; i < N; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] * i < N; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
    for (int i = 0; i <cnt; i++)//满足条件的i,j中,i和j必有一个是2
        if (!st[primes[i] + 2])//判断每一个素数+2是否符合
            a[primes[i] + 2] = 1;
    for (int i = 1; i < N; i++)
        a[i] += a[i - 1];
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin>>t;
    pre();//预处理出所有数据,若查询一个处理一个,会超时
    while(t--)
    {
        int n;
        cin>>n;
        cout<<a[n]<<endl;
    }
    return 0;
}

B.WUST周赛一

分析:我们只需要被删除的元素尽量小即可,因此排个序之后删除前一半元素,计算后一半元素剩下的值的和即可。

贪心:让最大值除1次2,倒数第二大值除2次2,以此类推……直到除2的次数超过32次或者已经选了一半的数为止。 

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
typedef long long ll;
int n;
ll a[N];
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    sort(a,a+n);
    ll res=0,cnt=n/2;
    for(int i=n/2;i<n;i++)
    {
        int x=cnt;
        while(x&&a[i]) a[i]/=2,x--;
        res+=a[i];
        cnt--;
    }
    cout<<res<<endl;
    return 0;
}

D.WUST周赛一 

分析:我们令 F(x) = f(x)e^{g(x)},那么F'(x) = f^{'}(x)e^{g(x)} + g^{'}(x)f(x)e^{g(x)} = [f'(x) + g'(x)f(x)]e^{g(x)}

通过观察我们不难发现:

1、不管对F(x)求多少次导数,e 的指数部分永远不会变

2、不管对F(x)求多少次导数,结果的形式永远可以化简为 h(x)e^{g(x)}的形式

那么我们假设 F^{m - 1}(x) = f(x)e^{g(x)},F^m(x) = h(x)e^g(x) 那么有:  


#include <bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f,mod=1e9 + 7;
//对多项式进行求导:
//1.如果输入的多项式f只有一个元素(即常数项),则返回只包含0作为常数项的导数
//2.否则,创建一个新的vector<int>类型的变量res,其大小为原多项式f的大小减1,用于存储求导后的多项式。
//3.遍历原多项式f中除了最后一项的所有元素,计算导数的值并存储到res中。导数的计算方法为将每一项的系数乘以该项的幂次,然后取模mod得到结果。最后返回求导后的多项式res。
vector<int> qiudao(vector<int> &f) 
{
    if(f.size() == 1) {
        vector<int> res;
        res.push_back(0);
        return res;
    }
    vector<int> res(f.size() - 1);
    for(int i = 0; i < f.size() - 1; i++) {
        res[i] = f[i + 1] % mod * (i + 1) % mod;
    }
    return res;
}
// 两个多项式相加
//1.首先创建一个新的vector<int>类型的变量res,其大小为输入的两个多项式f和g中较大的多项式的大小,用于存储相加后的多项式。
//2.遍历两个多项式f和g中的元素,对应位置上的系数相加,并将结果存储到res中。由于res的大小取两个多项式中较大的那个,因此遍历条件为i < f.size() || i < g.size()。
//3.在每次相加时,需要取模mod以防止结果溢出。最后返回相加后的多项式res。
vector<int> add(vector<int> f,vector<int> g) 
{
    vector<int> res(max(f.size(),g.size()));
    for(int i = 0; i < f.size() || i < g.size(); i++) {
        if(i < f.size())
            res[i] = (f[i] + res[i]) % mod;
        if(i < g.size())
            res[i] = (g[i] + res[i]) % mod;
    }
    return res;
}
// 两个多项式相乘
//1.首先创建一个新的vector<int>类型的变量res,其大小为输入的两个多项式f和g的大小之和加1,用于存储相乘后的多项式。
//2.使用两重循环遍历两个多项式f和g中的所有元素,并将对应位置上的系数相乘后加到res中合适的位置。具体来说,第i项和第j项的乘积将会加到res的第i+j项上。
//3.在每次相乘并累加时,需要取模mod以防止结果溢出。最后返回相乘后的多项式res。
vector<int> mul(vector<int> f,vector<int> g) 
{
    vector<int> res(f.size() + g.size());
    for(int i = 0; i < f.size(); i++) {
        for(int j = 0; j < g.size(); j++) {
            res[i + j] = (f[i] * g[j] + res[i + j]) % mod;
        }
    }
    return res;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n,m;
    cin >> n >> m;
    vector<int> f,g;
    for(int i = 1; i <= n; i++) {
        int x;
        cin >> x;
        g.push_back(x);
    }
    if(m == 0) {  // m = 0特判
        cout << 1 << endl;
        return 0;
    }
    f.push_back(1);
    vector<int> res = f;
    for(int i = 1; i <= m; i++) {
        res = add(qiudao(res),mul(qiudao(g),res));
        // h(x) = [f'(x) + g'(x)f(x)]
    }
    while(res.size() > 1 && res.back() == 0)
        res.pop_back();
    // 别忘了去除末尾多余的0
    for(auto t : res) {
        cout << t << " ";
    }
    cout << endl;
    return 0;
}

 E.WUST周赛一 

分析:计算这个数列中,出现次数为奇数个的数字的异或和

------->只需计算所有数字的异或和即可,因为出现次数为偶数的数总会和自身进行异或为0

tips:异或运算(XOR)具有一些重要的性质

  1. 自反性质:a ⊕ a = 0,即一个数和自己进行异或操作的结果为0。

  2. 零值性质:a ⊕ 0 = a,任何数和0进行异或操作的结果为它本身。

  3. 结合性质:异或运算满足结合律,即 (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)。

  4. 交换性质:异或运算满足交换律,即 a ⊕ b = b ⊕ a。

  5. 相同数值的异或结果为0:a ⊕ a = 0。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n;
    scanf("%d",&n);
    int sum=0;
    for(int i=1;i<=n;++i)
    {
        int x;
        scanf("%d",&x);
        sum^=x;
    }
    printf("%d",sum);
    return 0;
}

F.WUST周赛一

分析:设f ( i )表示有i个位置的方案数,因为当第i个位置放A时,从i − 1递推过来,放B时,第i − 1个只能放B,所以从i − 2递推过来。则有f[i]=f[i-1]+f[i-2],即为斐波拉契数列

因为数据范围较大,我们可以用矩阵快速幂的方法来加速求斐波拉契数列

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
struct matrix
{
    ll c[3][3];
    matrix(){memset(c,0,sizeof c);}
}F,A;//F为斐波那契矩阵,A为构造矩阵
matrix operator*(matrix &a,matrix &b)//矩阵乘法
{
    matrix t;
    for(int i=1;i<=2;++i)
        for(int j=1;j<=2;++j)
            for(int k=1;k<=2;++k)
                t.c[i][j]=(t.c[i][j]+a.c[i][k]*b.c[k][j])%mod;
    return t;
}
void quickpow(ll n)//快速幂
{
    F.c[1][1]=2,F.c[1][2]=1;
    A.c[1][1]=A.c[1][2]=A.c[2][1]=1;
    while(n)
    {
        if(n&1) F=F*A;
        A=A*A;
        n>>=1;
    }
}
int main()
{
    int T;scanf("%d",&T);
    while(T--)
    {
         ll n; 
         cin>>n;
         if(n==1)//特判
         {
             cout<<1<<endl;
             continue;
         }
         if(n==2)//特判
         {
             cout<<2<<endl;
             continue;
         }
         memset(A.c,0,sizeof A.c);//每一次操作完后都要重置
         quickpow(n-2);
         cout << F.c[1][1]<<endl;
    }
    return 0;
}

G.WUST周赛一 
 

分析:1.求区间和------->前缀和

           2.分析条件,需要寻找的是区间和为完全平方数的区间,那就可以直接枚举完全平方数。

              根据s[r]-s[l-1]=j*j,可知s[r]-j*j=s[l-1],这样,我们可以通过更新满足条件的前缀和的个数,                  不断进行累加,即可得到答案

tips:

  1. cnt开N*10-------->前缀和最大为N*10
  2. 错误示范
   for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        s[i]=s[i-1]+a;
        cnt[s[i]]++;//为什么不能提前预处理cnt数组
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j*j<=s[i];j++)
            ans+=cnt[s[i]-j*j];
    }
//这个cnt是记录该位置前面的数的数量
//比如s数组1 2 3 4 5,到第五个数,前面1,4符合条件,ans+2
//再比如1 2 3 5 4,到第4个数应该只有1,你如果提前把4加进去的话就是1 4,那么第四个是就找了5 4,到第五个数又有一组5 4符合,这样对于5 4这组数,同一个区间你找了两次

正确AC代码: 

#include<iostream>
#include<algorithm>
using namespace std;
int n;
const int N=1e5+10;
long long cnt[N*10],s[N],ans;
int main()
{
    cin>>n;
    cnt[0]=1;
    for(int i=1;i<=n;i++)
    {
        int a;
        cin>>a;
        s[i]=s[i-1]+a;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j*j<=s[i];j++)
            ans+=cnt[s[i]-j*j];
        cnt[s[i]]++;
    }
    cout<<ans<<endl;
    return 0;
}

H.WUST周赛一 

思想:分层图最短路 

分层图最短路是指在可以进行分层图的图上解决最短路问题。

分层图:可以理解为有多个平行的图。

一般模型是:在一个正常的图上可以进行 k 次决策,对于每次决策,不影响图的结构,只影响目前的状态或代价。一般将决策前的状态和决策后的状态之间连接一条权值为决策代价的边,表示付出该代价后就可以转换状态了。

一般有两种方法解决分层图最短路问题:

  1. 建图时直接建成k+1层。
  2. 多开一维记录机会信息。

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> pii;
const int N=1500050;
const int INF=0x3f3f3f3f;
int h[N],e[N*2],ne[N*2],idx,w[N*2];
int n,m,k,st,ed;
int dis[N];
bool s[N];
void addedge(int a,int b,int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
void dijkstra(int st,int ed)
{
    memset(dis,0x3f,sizeof(dis));
    dis[st]=0;
    priority_queue<pii,vector<pii>,greater<pii>> q;
    q.push({0,st});
    while(q.size())
    {
        auto t=q.top();
        q.pop();
        int ver=t.second,distance=t.first;
        if(s[ver]) continue;
        s[ver]=true;
        for(int i=h[ver];i!=-1;i=ne[i])
        {
            int j=e[i];
            if(dis[j]>distance+w[i])
            {
                dis[j]=distance+w[i];
                q.push({dis[j],j});
            }
        }
    }
    int ans=INF;
    for(int i=0;i<=k;i++)
    {
        ans=min(ans,dis[i*n+ed]);
    }
    printf("%d\n",ans);
}
int main()
{
    memset(h,-1,sizeof(h));
    scanf("%d %d %d %d %d",&n,&m,&k,&st,&ed);
    for(int i=1;i<=m;i++)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        for(int j=0;j<=k;j++)
        {
            addedge(a+j*n,b+j*n,c);//构造每一层
            addedge(b+j*n,a+j*n,c);
            if(j!=k)
            {
                addedge(a+j*n,b+(j+1)*n,0);//每层的权值为0
                addedge(b+j*n,a+(j+1)*n,0);
            }
        }
    }
    dijkstra(st,ed);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值