“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛

下面是部分题的题解。


前五题应该没什么太大问题,只贴上我比赛时的代码


第一题:黑白图像直方图


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 1005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int a[maxn][maxn];
int ans[maxn];

int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        mst(ans,0);
        for(int i=0;i<n;i++)
        for(int j=0;j<m;j++)
        {
            scanf("%d",&a[i][j]);
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                ans[i]+=(a[j][i]==1);
            }
        }
        for(int i=0;i<m-1;i++)
        {
            printf("%d ",ans[i]);
        }
        printf("%d\n",ans[m-1]);
    }
    return 0;
}


第二题:神无月排位赛


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

char c[5]= {'D','C','B','A','S'};

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int sum=0;
        int now=0;
        int a,b,c;
        int x;
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&x);
            if(x==1)
                sum+=10;
            else sum-=5;
            if(sum<0)
                sum=0;
            if(sum>=100)
            {
                if(i<n)
                {
                    i++;
                    scanf("%d",&a);
                    if(i<n)
                    {
                        i++;
                        scanf("%d",&b);
                        if(a==1&&b==1)
                        {
                            now++;
                            sum=0;
                        }
                        else if(a==0&&b==0)
                        {
                            sum=60;
                        }
                        else
                        {
                            if(i<n)
                            {
                                i++;
                                scanf("%d",&c);
                                if(c==1)
                                {
                                    now++;
                                    sum=0;
                                }
                                else
                                {
                                    sum=60;
                                }
                            }
                        }
                    }
                }
            }
        }
        if(now>4) now=4;
        if(now==0) puts("D");
        if(now==1) puts("C");
        if(now==2) puts("B");
        if(now==3) puts("A");
        if(now==4) puts("S");
    }
    return 0;
}


第三题:I Love ces


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int a[10];
char s[maxn];

//i  love ces

int main()
{
    while(~scanf("%s",s))
    {
        mst(a,0);
        int len=strlen(s);
        for(int i=0;i<len;i++)
        {
            if(s[i]=='i'||s[i]=='I')
                a[0]++;
            if(s[i]=='l'||s[i]=='L')
                a[1]++;
            if(s[i]=='o'||s[i]=='O')
                a[2]++;
            if(s[i]=='v'||s[i]=='V')
                a[3]++;
            if(s[i]=='e'||s[i]=='E')
                a[4]++;
            if(s[i]=='c'||s[i]=='C')
                a[5]++;
            if(s[i]=='s'||s[i]=='S')
                a[6]++;
        }
        a[4]/=2;
        sort(a,a+6);
        printf("%d\n",a[0]);
    }
    return 0;
}


第四题:添加好友


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 105;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

ll n;

ll fast_mod(ll a,ll b,ll c)
{
    ll ans=1;
    a%=c;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%c;
        b>>=1;
        a=(a*a)%c;
    }
    return ans;
}


int main()
{
    while(~scanf("%lld",&n))
    {
        ll ans=fast_mod(2,n,mod);
        ans=(ans-1+mod)%mod;
        printf("%lld\n",ans);
    }
    return 0;
}


第五题:字符串进制转换


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

ll power(ll a, ll n)
{
    ll ans=1;
    while(n)
    {
        if(n&1) ans*=a;
        a*=a;
        n>>=1;
    }
    return ans;
}

/*ll solve(ll n,ll x)
{
    return n-(x*(n/x));
}*/

char s[maxn];
int a[maxn];
ll m;

int main()
{
    while(~scanf("%s%lld",s,&m))
    {
        ll ans=0;
        int len=strlen(s);
        if(len==1&&s[0]=='a')    //特别注意这个特判
        {
            puts("0");
            continue;
        }
        for(int i=0;i<len;i++)
        {
            ans+=(s[len-1-i]-'a')*power(26,i);
        }
        int cnt=0;
        while(ans)
        {
            a[cnt++]=ans%m;
            ans/=m;
        }
        for(int i=cnt-1;i>=0;i--)
        {
            printf("%d",a[i]);
        }
        puts("");
    }
    return 0;
}


第六题:

A序列

时间限制: 1000ms   内存限制: 128M

 

如果一个序列有奇数个正整数组成,不妨令此序列为 a 1 , a2 , a3 , . . . , a2k+1 ( 0<=k ),并且 a 1 , a2 . . . ak +1 是一个严格递增的序列, a k +1 , ak +2 , . . . , a2k+1 ,是一个严格递减的序列,则称此序列是A序列。

比如1 2 5 4 3就是一个A序列。

现在Jazz有一个长度为 n 的数组,他希望让你求出这个数组所有满足A序列定义的子序列里面最大的那个长度。(子序列可以不连续)

比如1 2 5 4 3 6 7 8 9,最长的A序列子串是1 2 5 43

 

多组输入,每组两行。
第一行是 n ,表示给的数组的长度。
第二行有 n 个数(int范围),即给你的数组。
1<=n<=500000

 

每组输入输出一行,即最长的A序列子串的长度。

1 

9

1 2 5 4 3 6 7 8 9

样例输出1 

5



【分析】根据题意我们可以枚举中间那个点,然后算出这个点左边(包含这个点)的最长单调子序列长度以及这个点右边(包含这个点)的长单调递减子序列长度取最小值就是要求的以该点为中心的A序列的最长长度的“一半”,每个点扫一遍即可。

求最长递增子序列用一下模板即可,求最长递减子序列只要逆过来求一下最长单调子序列就好了。


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 50005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int num[maxn];
int temp[maxn];
int ans[maxn];
int l[maxn];
int r[maxn];

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&num[i]);
        }
        int Max=1;
        mst(temp,0x3f);
        temp[0]=-1;
        for(int i=1; i<=n; i++)      //求最长单调递增子序列
        {
            int pos=lower_bound(temp,temp+n,num[i])-temp;
            temp[pos]=num[i];
            Max=max(Max,pos);
            l[i]=Max;
        }
        Max=1;
        mst(temp,0x3f);
        temp[0]=-1;
        for(int i=n; i>=1; i--)     //求单调递减子序列
        {
            int pos=lower_bound(temp,temp+n,num[i])-temp;
            temp[pos]=num[i];
            Max=max(Max,pos);
            r[i]=Max;
        }
        int ans=1;
        for(int i=1;i<=n;i++)
        {
            ans=max(ans,min(l[i],r[i]));
        }
        printf("%d\n",ans*2-1);
    }
    return 0;
}

第七题:


战斗

时间限制: 2000ms   内存限制: 128M

 

最近,盛大计划开发一款手游,以下是简化版。系统和我方各有 n 头怪兽,每一头怪兽都有生命值和攻击力,并且当怪兽A攻击怪兽B,如果怪兽B的生命值高于怪兽A的攻击力,则怪兽B的生命力减少A的攻击力的数值,否则怪兽B将死亡。我方已经通过一些手段得知了系统怪兽的出战序列,我方想要知道,我方是否可以合理安排怪兽的出战序列,保证当系统的 n 头怪兽全部死亡时,而我方至少还存在一头怪兽。

所有怪兽是每秒攻击一次,即如果AB战斗,A受到B的伤害的同时,B也受到A的伤害,直到一方死亡,换序列中的下一个怪兽,继续战斗。

 

第一行一个整数 T ,表示测试组数。
对于每组数据,第一行输入一个整数 n 1<=n<=10 ,表示怪兽的数目。
接下来 n 行,表示系统 n 头怪兽的出战序列,每一行两个整数 v , a , 1<=v<=1000 , 1<=a<=100 .其中 v 表示生命值, a 表示攻击力。
接下来 n 行,表示我方 n 头怪兽,但是出战序列可以由我方自己安排。每行两个整数,含义类似。

 

每组数据输出一行。如果我方可以通过合理安排怪兽的出战序列,保证当系统的 n 头怪兽全部死亡,而我方至少还存在一头怪兽,那么输出YES;否则输出NO


1 

2
2
5 4
4 3
3 2
5 4
2
5 4
4 3
3 2
5 5

1 

NO
YES



【分析】(首先想吐槽一句,这游戏情景不是炉石吗??!)。看到这道题,n<=10,想到用暴力dfs,把我方所有有可能出场顺序枚举一遍,再对某一种方案判断是否可行。

一般来说要写个dfs决定所有有可能的出场顺序,但这里可以偷个小懒,可以用STL库中的next_permutation()函数来实现出场顺序的全排列,具体实现过程为从序列的最小表示开始,依次变大,直到成为最大表示结束。如序列(1,2,3),从(1,2,3)变为(1,3,2)逐渐增大直到(3,2,1)结束。还有一个prev_permutation()函数,功能正好相反。

其他具体细节见代码。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 15;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int order[maxn]= {0,1,2,3,4,5,6,7,8,9};  //出场顺序
int blood1[maxn],blood2[maxn];
int cost1[maxn],cost2[maxn];
int temp1[maxn],temp2[maxn];
int n,flag;

bool judge(int *a)
{
    /*for(int i=0;i<n;i++)
    {
        printf("%d ",a[i]);
    }
    puts("");*/
    for(int i=0; i<n; i++)           //由于每次都要改变blood的值,所以要注意初始化
    {
        blood1[i]=temp1[i];
        blood2[i]=temp2[i];
    }
    int i=0,j=0;
    while(i<n&&j<n)
    {
        int xx=min(blood1[i]/cost2[order[j]],blood2[order[j]]/cost1[i]);  //优化,减少循环次数
        blood1[i]-=(cost2[order[j]]*xx);
        blood2[order[j]]-=(cost1[i]*xx);
        while(blood1[i]>0&&blood2[order[j]]>0)
        {
            blood1[i]-=cost2[order[j]];
            blood2[order[j]]-=cost1[i];
        }
        if(blood1[i]<=0)
        {
            i++;
        }
        if(blood2[order[j]]<=0)
        {
            j++;
        }
    }
    if(i==n&&j<n) flag=1;       //系统的怪兽死完了,但我方还有怪兽存活
    return flag;
}


int main()
{
    rush()
    {
        scanf("%d",&n);
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&blood1[i],&cost1[i]);
            temp1[i]=blood1[i];
        }
        for(int i=0; i<n; i++)
        {
            scanf("%d%d",&blood2[i],&cost2[i]);
            temp2[i]=blood2[i];
        }
        flag=0;
        do
        {
            if(judge(order))
            {
                flag=1;
                break;
            }
        }
        while(next_permutation(order,order+n)); //全排列
        if(flag) puts("YES");
        else puts("NO");
    }
    return 0;
}

第八题:

调和序列

时间限制: 1000ms   内存限制: 128M

 

给定一个长度为 n 的非负整数序列,下标为 0 , 1 ,…, n 1

定义: sequence ( K ) 由下标为 K 的倍数组成的子序列,即下标为 0 , K , 2K ,..., [n1/k]k

query(K, S ) :询问 sequence ( K ) 中的第 S 大的数字

 

第一行一个整数 T ,表示测试组数。
对于每组数据,第一行输入两个整数 n , m 1<=n<=20000 , 1<=m<=100000 n 表示序列的长度, m 表示询问个数。
接下来一行是 n 个整数 a 0 , a1 , . . , an 1 , 0 <= ai <

231

, i =0,1,, n 1 ,表示序列。
接下来 m 行,每行两个整数 K , S
0 < K <=109 , 1<=S<=n

 

每组数据对于每个询问输出一行,若 sequence ( K ) 的元素个数小于 S ,输出 1 ;否则输出 query ( K , S )

1 

1
5 2
2 5 3 4 1
2 4
2 1

1 

-1
 3

 

 
【分析】先用vector容器预处理一下即可,注意过程中的优化。


#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 20005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

vector<int>vec[maxn];
int a[maxn];
bool flag[maxn];
int n,m;

void init()
{
    for(int i=1;i<n;i++)
    {
        for(int j=0;j<n;j+=i)
            vec[i].push_back(a[j]);
    }
}

int main()
{
    rush()
    {
        mst(flag,0);
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            vec[i].clear();
        }
        init();
        int k,s;
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&k,&s);
            if(k>=n)
            {
                if(s==1) printf("%d\n",a[0]);
                else puts("-1");
                continue;
            }
            if(vec[k].size()<s)    //优化1
            {
                puts("-1");
                continue;
            }
            if(flag[k]==0)         //优化2
            {
                sort(vec[k].begin(),vec[k].end());
                flag[k]=1;
            }
            printf("%d\n",vec[k][vec[k].size()-s]);
        }
    }
    return 0;
}



第九题:


丢史蒂芬妮

时间限制: 1000ms   内存限制: 128M

 

有一天,空和白很无聊,决定玩盛大游戏,考虑到两个人玩,他们随便掏了一个游戏出来:在一个 n m 的棋盘上,首先把史蒂芬妮·多拉放在左上角 (1,1) 的位置。每次一个人可以将她往下,往右,往右下丢一格。当前回合,谁不能丢史蒂芬妮,谁就输了。(注意,不可以把活人丢出棋盘啦!)游戏总是空先手。

白说,这是一个垃圾游戏!我们每次把史蒂芬妮丢素数个位置吧!(换句话说,每次丢 2 3 5 7 格)空答应了。

我们都知道,空和白都很聪明,不管哪方存在一个可以必胜的最优策略,都会按照最优策略保证胜利。

玩了一局,空已经知道了这个游戏的套路,现在他决定考考你,对于给定的 n m ,空是赢是输?如果空必胜,输出“Sora”(无引号);反之,输出“Shiro”(无引号)。

 

第一行有一个T表示数组组数, 1<=T<100000
从第二行开始,每行为棋盘大小, n m 分别表示行列。
1=<n<=500 1=<m<=500

 

对于每组数据,按题目要求输出。

1 

4
1 1
2 2
10 10
30 30

1 

Shiro
Shiro
Shiro
Sora



【分析】这个博弈题其实就是必胜态与必败态的转化,比赛的时候没去仔细想。。。其实知道两句话即可:

  1. 如果一个点能够走到必败点,那它就是必胜点
  2. 如果一个点走不到必败点,只能走到必胜点,这个点就是必败点。

然后从终点开始推回起点,得到每个点是必胜态还是必胜态。但开始在线查询,一组数据推一遍结果超时,再看了下测试数据组数,非常大,而且如果每次递推的话会有很多冗余的地方,改为先预处理再离线查询就16ms AC了。


#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 500;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int prime[maxn+5]= {0};
int num_prime=0;
bool isprime[maxn+5]= {1,1};
int vis[maxn+5][maxn+5];

void init()
{
    for(int i=2; i<maxn+5; i++)
    {
        if(!isprime[i])
            prime[num_prime++]=i;
        for(int j=0; j<num_prime&&i*prime[j]<maxn+5; j++)
        {
            isprime[i*prime[j]]=1;
            if(i%prime[j]==0)
                break;
        }
    }
    mst(vis,0);                     //1:必胜 2:必败
    vis[1][1]=2;
    for(int i=1; i<=maxn; i++)
    {
        for(int j=1; j<=maxn; j++)
        {
            if(vis[i][j]==0)
            {
                vis[i][j]=2;
            }
            if(vis[i][j]==2)
            {
                for(int k=0; ; k++)
                {
                    if(i+prime[k]>maxn&&j+prime[k]>maxn)
                        break;
                    if(i+prime[k]<=maxn)
                    {
                        vis[i+prime[k]][j]=1;
                    }
                    if(j+prime[k]<=maxn)
                    {
                        vis[i][j+prime[k]]=1;
                    }
                    if(i+prime[k]<=maxn&&j+prime[k]<=maxn)
                    {
                        vis[i+prime[k]][j+prime[k]]=1;
                    }
                }
            }
        }
    }
}


int main()
{
    init();
    int n,m;
    rush()
    {
        scanf("%d%d",&n,&m);
        if(vis[n][m]==1) puts("Sora");
        else puts("Shiro");
    }
    return 0;
}



第十题:


膜一下将带给你好运

时间限制: 1000ms   内存限制: 128M

 

欧拉函数 ϕ ( n ) 被定义 1 ~ n 中与 n 互质的数的个数。例如 ϕ (5)=4 ,因为 1 , 2 , 3 , 4 这四个数字与 5 互质。

定义 f 函数:


 

其中 [n/i] 表示 n 除以 i 所得到的商

 

第一行一个整数 T ,表示测试组数。对于每组数据,输入一行,包含一个数字 n 466<=n<=108

 

每组数据输出一行,表示函数值 f ( n ) 1000000007 取模

1

2
1068
972

1 

293824
222698



【分析】由于一般要处理多个数的欧拉函数值,数量级只能到10^6数量级,所以题目所给的1e8显然无法通过这种方法能到欧拉函数值。正确做法是先预处理出一定范围内的欧拉函数值,然后再根据杜教筛求解,在计算结果时可以用分块加速。

由于对这方面见解不深刻,先给出代码。


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 5000005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int res[maxn]= {0};
ll sum[maxn]= {0};

void euler()
{
    res[1]=1;
    for(int i=2; i<maxn; i++)
    {
        if(!res[i])
        {
            for(int j=i; j<maxn; j+=i)
            {
                if(!res[j])
                    res[j]=j;
                res[j]=res[j]/i*(i-1);
            }
        }
    }
    for(int i=1; i<maxn; i++)
    {
        sum[i]=(sum[i-1]+res[i])%mod;
    }
}

ll fun(ll x)
{
    if(x<maxn)return sum[x];
    ll ans=0;
    int l,r;
    for(int i=2; i<=x; i=r+1)
    {
        l=i;
        r=x/(x/i);
        ans=(ans+fun(x/i)*(r-l+1)%mod)%mod;
    }
    ll temp=(x*(x+1)/2-ans+mod)%mod;
    return temp;
}

int main()
{
    euler();
    ll n;
    rush()
    {
        scanf("%lld",&n);
        ll ans=0;
        ll gap=n-233;
        ll l,r;
        for(int i=233; i<=gap; i=r+1)
        {
            l=i;
            r=min(n/(n/i),gap);
            ll temp=(fun(r)-fun(l-1)+mod)%mod;
            temp=(temp*(n/i))%mod;
            ans=(ans+temp)%mod;
        }
        printf("%lld\n",ans);
    }
    return 0;
}



第十一题:


购买装备

时间限制: 1000ms   内存限制: 128M

 

最近盛大的一款游戏传奇世界极其火爆。游戏玩家John,想购买游戏中的装备。已知游戏的商店里有 n 件装备,第 i 件装备具有属性值 a i ,购买需要花费 b i 个金币。John想去购买这些装备,但是账号中只有 m 个金币,John是个很贪婪的家伙,他想购买尽可能多的装备。并且在保证购买到最多件装备的情况下,他还想让他所购买的装备当中拥有最小属性值的装备属性值尽可能大

 

输入测试组数 T ,每组数据第一行输入整数 n ( 1<=n<=100000 ) m ( 1<=m<=109 ),接下来有 n 行,第 i 行有两个数 a i , b i ( 1<=ai , b i <= 10000 ).

 

对于每组数据,输出两个数字,第一个数字代表John最多可以购买的装备数,第二个数代表在John购买最多件装备的前提下,所购买的装备当中拥有最小属性值的装备的最大属性值(输入数据保证至少可以购买一件装备)

1 

1
2 4
3 2
2 3

1 

1 3

 


【分析】我们可以用贪心的方法先算出可以购买装备的最大数量,然后再去二分属性值,得到在装备购买数量最大的情况下属性值的最大值。


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

struct node
{
    int w,cost;
}a[maxn];

int n,m;
int num;

bool cmp(const node &a,const node &b)
{
    if(a.cost==b.cost)
        return a.w>b.w;
    return a.cost<b.cost;
}

int judge(int x)
{
    int sum=0;
    int Count=0;
    for(int i=0;i<n;i++)
    {
        if(a[i].w>=x)
        {
            Count++;
            sum+=a[i].cost;
        }
        if(Count==num)
            break;
    }
    if(Count!=num) return 0;
    if(sum<=m) return 1;
    return 0;
}

int main()
{
    rush()
    {
        scanf("%d%d",&n,&m);
        int temp=m;
        int Min=INF;
        int Max=0;
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&a[i].w,&a[i].cost);
            Min=min(a[i].w,Min);
            Max=max(a[i].w,Max);
        }
        sort(a,a+n,cmp);
        num=0;
        while(m>=a[num].cost)    //得到可以购买装备的最大数目
        {
            m-=a[num].cost;
            num++;
            if(num==n)
                break;
        }
        m=temp;
        int l=Min,r=Max;
        int ans;
        while(l<=r)             //二分属性值区间
        {
            int mid=(l+r)/2;
            if(judge(mid))      //判断是否存在这个属性值,能取到num件装备,且总花费小于总的金币数
            {
                ans=mid;
                l=mid+1;
            }
            else r=mid-1;
        }
        printf("%d %d\n",num,ans);
    }
    return 0;
}


第十三题:


风力观测

时间限制: 1000ms   内存限制: 128M

 

Y正在观测y地区的风力情况,他在一条直线上依此设定了 n 个观测点,并观测与直线垂直方向的风力值,风力有时是正向的也有时是反向的,规定正向时的风力值为正数,他发现每次风力值的变化都可以表示为观测点上一条线段 [L,R] 上的同时增强或者减弱。小Y希望能够实时统计这些观测点的数据,并且实时分析这些观测点在历史中到达的风力最大绝对值,但是他无法同时对大量的观测点进行分析,更重要的是他记不住这些观测点过去的风力大小,于是他希望你来用计算机帮助他完成这个任务。

你简化了这个问题,将问题分为两种查询:

1.对观测点 [L,R] 上的风力正向增强 X 。( X 为负数表示正向减弱,即反向加强)

2.查询观测点 A 上的历史风力最大绝对值。

 

第一行有一个整数 T 表示数据组数。( T <= 10
接着有 T 组数据,每组数据第一行是整数 n q ,表示观测点个数和查询次数。
第二行有 n 个数 a 1 , . . . , an ,表示每个观测点的风力初始值。
接着有 q 行,表示 q 次操作,格式为:
1 L R X
:表示对 [LR] 线段上的正向风力同时增强 x
2 A
:表示查询 A 点的历史风力最大绝对值。
1<=n,q<=100000
1<=L,R,A<=n
10000<=ai , X <= 10000

 

对每次询问2,输出一个数字表示风力值并换行。

1 

1
5 6
1 -1 2 3 -3
1 1 5 1
2 1
2 2
1 2 4 -5
2 2
2 3

1 

2
1
5
3



【分析】这题应该是线段树题目里有点难度的题吧。我们先初始化线段树上每个点的的值为0,然后进行更新操作,并用Max[i]和Min[i]分别表示i这个结点的历史最大值和历史最小值。每次查询i节点时的结果ans即为:

ans=max(abs(a[i]+Min[i]),abs(a[i]+Max[i])).

值得注意的是,每次pushdown时需要更新以下值(当前节点为x,其父节点为fa)

Min[x]=min(Min[x],lazy[x]+Min[fa])

Max[x]=min(Max[x],lazy[x]+Max[fa])

lazy[x]+=lazy[fa]

同时要把关于父节点的数据清零(很好理解,因为已经传下去了嘛)。


#include <cstdio>
#include <cmath>
#include <iostream>
#include <queue>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

typedef long long ll;
typedef pair<int,int>P;
const int maxn= 100005;
const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

int Max[maxn<<2],Min[maxn<<2],lazy[maxn<<2];
int a[maxn];

void pushdown(int rt)
{
    Max[rt<<1]=max(Max[rt<<1],lazy[rt<<1]+Max[rt]);
    Min[rt<<1]=min(Min[rt<<1],lazy[rt<<1]+Min[rt]);
    Max[rt<<1|1]=max(Max[rt<<1|1],lazy[rt<<1|1]+Max[rt]);
    Min[rt<<1|1]=min(Min[rt<<1|1],lazy[rt<<1|1]+Min[rt]);
    lazy[rt<<1]+=lazy[rt];
    lazy[rt<<1|1]+=lazy[rt];
    Min[rt]=Max[rt]=lazy[rt]=0;
}

/*void build(int l,int r,int rt)
{
    lazy[rt]=Max[rt]=Min[rt]=0;
    if(l==r)
        return;
    int m=(l+r)/2;
    build(lson);
    build(rson);
}*/


void update(int x,int y,int val,int l,int r,int rt)
{
    if(x<=l&&r<=y)
    {
        lazy[rt]+=val;
        Max[rt]=max(Max[rt],lazy[rt]);
        Min[rt]=min(Min[rt],lazy[rt]);
        return;
    }
    if(Max[rt]||Min[rt]||lazy[rt])
        pushdown(rt);
    int m=(l+r)/2;
    if(x<=m)   update(x,y,val,lson);
    if(y>m)  update(x,y,val,rson);

}

P query(int pos,int l,int r,int rt)
{
    if(l==r)
        return P(Max[rt],Min[rt]);
    if(Max[rt]||Min[rt]||lazy[rt])
        pushdown(rt);
    int m=(l+r)/2;
    if(pos<=m)  return query(pos,lson);
    return query(pos,rson);
}

int main()
{
    int n,q;
    rush()
    {
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
        }
        //build(1,n,1);
        mst(lazy,0);
        mst(Max,0);
        mst(Min,0);
        int order,l,r,x;
        for(int i=0;i<q;i++)
        {
            scanf("%d",&order);
            if(order==1)
            {
                scanf("%d%d%d",&l,&r,&x);
                update(l,r,x,1,n,1);
            }
            else if(order==2)
            {
                scanf("%d",&x);
                P ans=query(x,1,n,1);
                printf("%d\n",max(abs(a[x]+ans.first),abs(a[x]+ans.second)));
            }
        }
    }
    return 0;
}


第十四题:


密码破解

时间限制: 1000ms   内存限制: 128M

 

近日来勒索病毒的事件频繁发生,小Y对它的加密原理非常感兴趣,研究了一番相关知识之后,他就来给你看他的加密程序,并给你一段密文,和你炫耀说就算把程序给你看你也破解不出来。

你扫了一眼代码发现加密的公式为 b = ae % m ,其中 e 是质数。

进一步分析发现 m = p q p q 都为质数, p != q

作为一个计算机高手,你早就对加密算法烂熟于心,一眼就看出这个程序的算法和原理,找到了破解的方法,发现小Y疏忽在与给了你一个不够大的 m

你知道解密的公式与加密对称,为 a = bd % m

但是你仍然无法心算解出这个 d ,因此你需要借助计算机来将密文破解。

 

第一行有一个整数 T 表示数据组数。( T <= 100
接着有 T 组数据,每组数据两行。
第一行有四个数 e p q n ,其中 e p q 如题所描述, n 表示需要解密的数字序列长度。
第二行是需要解密的数字序列 a 1 . . an
1 <p, q , e <=108 p q e 为质数且 p != q 。保证解密的结果即原数列的值小于 min ( p , q ) 并大于等于 0
1<=n<=100
保证 m 有且仅有两个不同的质因数 p q ,并且一定存在一个题中描述的参数 d 使得解密公式能够无损解密出所有 0 ~ min ( p , q ) 1 范围之间的数字。

 

对于每组数据输出一行,表示解密后的数字序列,数字之间以空格隔开。

1 

1
5 19 29 3
335 440 514

1 

65 67 77

 

对于样例,存在d=101使得解密公式成立。

注意mai的大小可能超过int的范围

 

【分析】这题之前一模一样做过,联想到了然而忘记了做法,该反思。具体解析见这里Problem G: L先生的回信2

 

主要思路是d*e==(恒等于)1mod(φ(m)),求逆元再用快速幂。


另外由于这里p,q互质,所以直接可以得到其phi值为(p-1)*(q-1)


这道题一开始就求了下逆元,然后用快速幂取余模板,但一直WA,怎么也找不出错误,后来才知道由于快速幂中的mod数可能很大,所以会爆long long,所以在快速幂的过程中套一个快速乘即可。


#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
#define rush() int T;scanf("%d",&T);while(T--)

typedef long long ll;
const int maxn= 1005;
//const ll mod = 1e9+7;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;

ll ans[maxn];

ll exgcd(ll a,ll b,ll &x,ll &y)
{
    if(b == 0)
    {
        x = 1,y = 0;
        return a;
    }
    ll d = exgcd(b,a%b,x,y);
    ll t = x;
    x = y,y = t - (a/b)*y;
    return d;
}

ll get_inv(ll a,ll Mod)
{
    ll x,y;
    exgcd(a,Mod,x,y);
    return (x%Mod+Mod)%Mod;
}

ll fun(ll a,ll b,ll c)    
{
    a%=c,b%=c;
    ll sum=0;
    while(b)
    {
        if(b&1)
            sum=(sum+a)%c;
        a=a*2%c;
        b>>=1;
    }
    return sum;
}

ll fast_mod(ll a,ll b,ll c)
{
    ll ans=1;
    a%=c;
    while(b)
    {
        if(b&1)
            ans=fun(ans,a,c);
        b>>=1;
        a=fun(a,a,c);
    }
    return ans;
}


int main()
{
    ll p,q,e;
    ll n;
    rush()
    {
        scanf("%lld%lld%lld%lld",&e,&p,&q,&n);
        ll m=p*q;
        ll temp=(p-1)*(q-1);    //φ(m)
        ll d=get_inv(e,temp);
        ll num;
        for(ll i=0; i<n; i++)
        {
            scanf("%lld",&num);
            ans[i]=fast_mod(num,d,m);
        }
        for(ll i=0; i<n-1; i++)
        {
            printf("%lld ",ans[i]);
        }
        printf("%lld\n",ans[n-1]);
    }
    return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值