“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

时间:2023-10-22 12:28:38

黑白图像直方图

发布时间: 2017年7月9日 18:30   最后更新: 2017年7月10日 21:08   时间限制: 1000ms   内存限制: 128M

描述

在一个矩形的灰度图像上,每个像素点或者是黑色的或者是白色的。黑色像素点用1表示,白色像素点用0表示。现在要求你编写一个程序,计算每列上黑色像素点的个数并输出。如下图所示是一个6∗8的黑板图像。

1 1 0 0 1 1 1 1 
0 1 1 0 1 0 1 0 
1 1 1 1 0 1 1 0 
0 1 1 0 0 1 0 0 
1 0 1 0 0 1 0 0 
0 1 0 1 0 1 1 0

输入

输入有多组组。
每组的第一行有2个整数m、n,(1<=m,n<=100)。
接着有m行,每行有n个数字,每个数字是0或1,之间用一个空格分开。

输出

对图像数据,依次一行输出图像中每列黑色像素点的总数。

样例输入1

3 5

1 1 0 0 1

0 1 1 0 1

1 1 1 1 0

6 8

1 1 0 0 1 1 1 1

0 1 1 0 1 0 1 0

1 1 1 1 0 1 1 0

0 1 1 0 0 1 0 0

1 0 1 0 0 1 0 0

0 1 0 1 0 1 1 0

样例输出1

2 3 2 1 2
3 5 4 2 2 5 4 1
分析:签到题吧,直接按照列打出来就好了!
下面给出AC代码:
 #include <bits/stdc++.h>
using namespace std;
int dp[][];
int main()
{
int n,m;
while(cin>>n>>m)
{
for(int i=;i<n;i++)
{
for(int j=;j<m;j++)
{
cin>>dp[i][j];
}
}
for(int i=;i<m;i++)
{
int ans=;
for(int j=;j<n;j++)
{
if(dp[j][i]==)
ans++;
}
if(i==m-) cout<<ans;
else cout<<ans<<" ";
}
cout<<endl;
}
return ;
}

神无月排位赛

发布时间: 2017年7月8日 21:06   最后更新: 2017年7月8日 22:35   时间限制: 1000ms   内存限制: 128M

描述

《神无月》作为盛大游戏2017年的全新原创大作,其开发团队在自研实力强大的传世工作室基础之上,还有美树本晴彦等日本一线知名画师及日本游戏音乐大师崎元仁加盟参与制作。目前正在不限号内测中,有很多玩家进入到神无月的世界中。

在神无月中,有着玩家之间切磋的排位赛,其段位主要分为五大段位,从低到高依次为:新兵、菁英、战将、统帅、王者。每个玩家只有从新兵段位慢慢努力,一点点晋级才能到达王者段位。成为一个王者是每一个玩家的追求和心愿。

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

假设神无月的段位系统如下:

从低到高的段位依次简记为:D、C、B、A、S。玩家打排位赛,每胜利1局增加10分,输1局扣除5分。每一个段位都需要积分,累计100分才可以进入晋级赛,晋级赛采用三局两胜制(3局中达到2局胜利就晋级成功,有2局失败就算晋级失败, 连胜或连败两局,第三局不需要打了)。晋级成功后,成为下一个段位,积分变为0,重新开始算分;如果晋级失败,则积分变为60,重新开始算分。为方便计算,如果该玩家一直输,积分降为0后,不再降分,也不会掉段位。

大圣同学最近对神无月非常喜欢,一直在努力成为王者。他从新兵0分开始打排位赛(刚开始处在段位D),他告诉你最近若干场比赛的最后胜利情况,请你写个算法猜猜他现在所处的段位。当段位到达S时,段位将不再提高。

输入

有若干组数据。
每组的第一行为一个N(0<N<500),表示有N场比赛数据。
第二行有N个数字,每个数字之间有空格隔开,每个数字代表每场比赛的输赢情况,1表示赢,0表示输。
注意:当第n场比赛结束时,若大圣同学正处于晋级赛,并且还无法决定晋级成功或失败,那么可以忽略这场晋级赛

输出

对于每组比赛数据,输出最后所处的段位的一个英文字符(D、C、B、A、S这五个段位中的一个)。

样例输入1

15
1 1 1 1 1 1 1 1 1 1 1 1 1 0 1
30
1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 1 1 1 1 1 1 1 1 0 1 1 1

样例输出1

C
B
分析:签到题,很简单,看看代码就会了!
下面给出AC代码:
 #include <bits/stdc++.h>
#define mod 1000000007
typedef long long ll;
using namespace std;
int m;
int s[];
int main(){
int len;
while(cin>>len){
for(int i=;i<len;i++) scanf("%d",s+i);
int dw=,jf=,flag=,p=;
for(int i=;i<len;i++){
if(flag>){
flag++;
if(s[i]==) p++;
else p--;
}else{
if(s[i]==) jf+=;
else jf-=;
}
if(jf>= && flag==){
flag++;
}
if(flag== || p>= || p<=-){
flag=;
if(p>) dw--,jf=;
else jf=;
p=;
}
if(jf<) jf=;
}
if(dw<) cout<<"S"<<endl;
else cout<<(char)(dw+'A')<<endl;
}
return ;
}

I Love ces

发布时间: 2017年7月8日 21:08   最后更新: 2017年7月8日 22:33   时间限制: 1000ms   内存限制: 128M

描述

时间过得好快啊,SHU计算机学院又有新的一批小朋友们进来了。2016级的同学想必也是非常喜欢计算机学院的,于是院长想测试一下大家对计算机的喜爱程度(院长大人别查我水表)。

院长给大家一行由大写字母(A-Z)和小写字母(a-z)组成的字符串,允许同学们在这个字符串里面任意挑选若干个字符,问最多可以组成多少个I LOVE CES(不区分大小写,没有空格,即只要有这8个字符就可以组成一个)。

输入

多组输入,每组一行,输入一个字符串。
字符串长度<=100000。

输出

每组输出一行答案,如题意。

样例输入1

IlovecesiLOVECES

样例输出1

2

分析:签到题,没什么好解释的,看代码好了!

下面给出AC代码:

 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=;
char s[maxn];
int ans[];
int main()
{
while(cin>>s)
{
int len=strlen(s);
memset(ans,,sizeof(ans));
for(int i=;i<len;i++)
{
if(s[i]=='I'||s[i]=='i')
ans[]++;
if(s[i]=='L'||s[i]=='l')
ans[]++;
if(s[i]=='O'||s[i]=='o')
ans[]++;
if(s[i]=='V'||s[i]=='v')
ans[]++;
if(s[i]=='E'||s[i]=='e')
ans[]++;
if(s[i]=='C'||s[i]=='c')
ans[]++;
if(s[i]=='S'||s[i]=='s')
ans[]++;
}
ans[]/=;
sort(ans+,ans+);
cout<<ans[]<<endl;
}
return ;
}

添加好友

发布时间: 2017年7月8日 21:10   最后更新: 2017年7月8日 22:32   时间限制: 1000ms   内存限制: 128M

描述

Tony最近喜欢上了龙之谷游戏,所以他想叫上他的好友组建一个公会来一起享受这款游戏。

Tony一共有n个好友,他可以叫上任意k(1<=k<=n)个好友来组建公会,并且所有好友都会答应他的请求。问Tony一共可以有多少种方案组建这个公会?

只要不是完全相同的人组建的方案视为不同方案,并且Tony至少要叫上一个人。

输入

多组输入,每组一行,输入一个正整数n(1<=n<=1000000000)。

输出

每组输出一行,输出方案数。(对1000000007取膜)

样例输入1

2

样例输出1

3

分析:就是一个快速幂的板子,抄下就好了!
下面给出AC代码:
 #include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=;
inline ll gcd(ll x,ll p)
{
ll cnt=;
for(;p;p>>=,x=x*x%mod)
{
if(p&)
cnt=cnt*x%mod;
}
return cnt;
}
int main()
{
ll n;
while(cin>>n)
{
ll ans=(gcd(,n)-)%mod;
cout<<ans<<endl;
}
return ;
}

字符串进制转换

发布时间: 2017年7月8日 21:13   最后更新: 2017年7月8日 22:31   时间限制: 1000ms   内存限制: 128M

描述

Claire Redfield在龙之谷游戏的一次任务中获得了一个上了锁的宝箱,上面刻了一串由小写字母构成的字符串A和一个数字m。

经过Claire长时间研究,他发现密码是和a,m有关的。字符串A相当于一个26进制的数字,a相当于0,b相当于1…….z相当于25。然后要将这个26进制的数转化成m进制那就是这个宝箱的密码。

Claire觉得这个太简单了所以要你帮她完成。

输入

多组输入,每组一行,输入一个字符串A和一个正整数m。
字符串长度<=10,2<=m<=9。

输出

每组输出一行答案,如题意。

样例输入1

b 2

样例输出1

1

分析:优先队列乱搞就好了!
 #include <bits/stdc++.h>
#define mod 1000000007
typedef long long ll;
using namespace std;
int m;
char s[];
int main(){
while(cin>>s>>m){
stack<int> st;
ll sum=;
for(int i=;i<strlen(s);i++){
sum=sum*+s[i]-'a';
}
while(sum){
st.push(sum%m);
sum/=m;
}
if(st.size()==){
cout<<;
}else{
while(st.size()){
cout<<st.top();st.pop();
}
}
cout<<endl;
}
return ;
}

官方题解:

 #include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; const int INF=0x3f3f3f3f;
const int maxn=; //
char ss[];
int a[];
int main()
{ int n,m,len;
long long s,x;
while (~scanf("%s%d",ss,&m))
{
len=strlen(ss);
x=; s=; for (int i=len-;i>=;i--)
{
s+=x*(ss[i]-'a');
x*=;
}
// printf("%lld\n",s);
n=;
if (s==) printf("");
else
{
while (s)
{
a[++n]=s%m;
s=s/m;
}
for (int i=n;i>=;i--) printf("%d",a[i]);
}
printf("\n");
} return ;
}

A序列

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M

描述

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

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

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

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

输入

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

输出

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

样例输入1

9

1 2 5 4 3 6 7 8 9

样例输出1

5

题目链接:http://acmoj.shu.edu.cn/contest/27/problem/110/

下面给出AC代码:

 #include <iostream>
#include <cstdio>
#include <vector>
#include <string>
#include <cstring>
#include <map>
#include <queue>
#include <algorithm>
using namespace std; const int INF=0x3f3f3f3f;
const int maxn=; //
int a[maxn],l[maxn],r[maxn],d[maxn];
int main()
{ int n,i,j,maxx,len; while (~scanf("%d",&n))
{
for (i=;i<=n;i++) scanf("%d",&a[i]);
d[]=a[]; len=; l[]=;
for (i=;i<=n;i++)
{
if (a[i]>d[len]) { d[++len]=a[i]; l[i]=len; }
else
for (j=;j<=len;j++)
if (d[j]>=a[i]) {d[j]=a[i]; l[i]=j; break; }
}
// for (i=1;i<=n;i++) printf("%d ",l[i]); printf("\n");
r[n]=; d[]=a[n]; len=;
maxx=;
for (i=n-;i>=;i--)
{
if (a[i]>d[len]) { d[++len]=a[i]; maxx=max(maxx,min(l[i],len)*-); }
else
for (j=;j<=len;j++)
if (d[j]>=a[i]) {d[j]=a[i]; maxx=max(maxx,min(l[i],len)*-); break; }
}
// for (i=1;i<=n;i++) printf("%d ",r[i]); printf("\n");
printf("%d\n",maxx);
} return ;
}

战斗

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 2000ms   内存限制: 128M

描述

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

所有怪兽是每秒攻击一次,即如果A和B战斗,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

题目链接:http://acmoj.shu.edu.cn/contest/27/problem/111/

题解思路:因为我们要用最少的血量大最多的攻击力所以将我方怪兽按攻击力从大到小排序然后生命力按从小到大排序然后跟系统怪兽对打判断是否能赢即可

下面给出AC代码:

 #include<iostream>
#include<cstring>
#include<ctime>
#include<cstdlib>
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
struct node{
int x,y;
bool operator<(node a)const{
if(y != a.y) return y > a.y;
return x < a.x;
}
}a[],b[];
int n;
int flag;
bool check(){
node b1[],b2[];
for(int i = ; i < n; i++){
b1[i] = a[i];
b2[i] = b[i];
}
int k = ,j = ;
for(;k<n&&j<n;){
int s1 = (b1[j].x-)/b2[k].y+;
int s2 = (b2[k].x-)/b1[j].y+;
if(s1>s2)
b1[j].x-=s2*b2[k].y,k++;
else if(s2>s1)
b2[k].x-=s1*b1[j].y,j++;
else k++,j++;
}
return k < n;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
for(int i = ; i < n; i++)
scanf("%d%d",&a[i].x,&a[i].y);
for(int i = ; i < n; i++)
scanf("%d%d",&b[i].x,&b[i].y);
sort(b,b+n);
check()?puts("YES"):puts("NO");
}
return ;
}

调和序列

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M

描述

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

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

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

输入

第一行一个整数T,表示测试组数。
对于每组数据,第一行输入两个整数n,m,1<=n<=20000, 1<=m<=100000,n表示序列的长度,m表示询问个数。
接下来一行是n个整数a0,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

题目链接:http://acmoj.shu.edu.cn/contest/27/problem/112/

题解思路:用vector容器存k>=1&&k

下面给出AC代码:

 #include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<set>
#include<vector>
using namespace std;
const int mx = ;
int n,m;
int a[mx];
vector<int>v[mx];
bool cmp(int a,int b){
return a>b;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++){
scanf("%d",&a[i]);
v[i].clear();
}
for(int i = ; i < n; i++){
for(int j = ; j < n; j+=i)
v[i].push_back(a[j]);
sort(v[i].begin(),v[i].end(),cmp);
}
while(m--){
int k,s;
scanf("%d%d",&k,&s);
if(k>=n){
if(s>) puts("-1");
else printf("%d\n",a[]);
}
else{
if(s>v[k].size()) puts("-1");
else printf("%d\n",v[k][s-]);
}
}
}
return ;
}

丢史蒂芬妮

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 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

题目链接:http://acmoj.shu.edu.cn/contest/27/problem/113/

 #include<bits/stdc++.h>
using namespace std; int z[],sg[][],vis[];
int main()
{
int cases,i,j,k,n,m,x,s,len;
bool flag;
z[]=; len=;
for (i=;i<=;i++)
{
flag=true;
for (j=;j*j<=i;j++)
if (i%j==) {flag=false; break;}
if (flag) z[++len]=i;
} sg[][]=;
for (i=;i<=;i++)
for (j=;j<=;j++)
if (!(i== && j==))
{
memset(vis,,sizeof(vis));
for (k=;k<=len;k++)
if (i-z[k]>= ||j-z[k]>=)
{
if (i-z[k]>=) vis[sg[i-z[k]][j]]=;
if (j-z[k]>=) vis[sg[i][j-z[k]]]=;
if (i-z[k]>= && j-z[k]>=) vis[sg[i-z[k]][j-z[k]]]=;
}
else break;
for (k=;k<=;k++)
if (!vis[k])
{
sg[i][j]=k;
break;
} }
while (~scanf("%d",&cases))
{
while (cases--)
{
scanf("%d%d",&n,&m); if (sg[n][m]) printf("Sora\n"); else printf("Shiro\n");
}
}

膜一下将带给你好运

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M

描述

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

定义f函数:

f(n)=∑i=233n−233ϕ(i)∗[n/i]

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

输入

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

输出

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

样例输入1

2

1068

972

样例输出1

293824

222698

下面给出AC代码:

 #include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <climits>
#include <cstring>
#include <string>
#include <set>
#include <bitset>
#include <map>
#include <queue>
#include <stack>
#include <vector>
#include <cassert>
#include <ctime>
#define rep(i,m,n) for(i=m;i<=(int)n;i++)
#define mod 1000000007
#define inf 0x3f3f3f3f
#define vi vector<int>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define ll long long
#define pi acos(-1.0)
#define pii pair<int,int>
#define sys system("pause")
#define ls rt<<1
#define rs rt<<1|1
const int maxn=1e6+;
const int N=5e2+;
using namespace std;
ll gcd(ll p,ll q){return q==?p:gcd(q,p%q);}
ll qpow(ll p,ll q){ll f=;while(q){if(q&)f=f*p%mod;p=p*p%mod;q>>=;}return f;}
int n,k,t;
ll phi[maxn];//phi[n]表示x<=n,y<=n这样的互质(x,y)对;
map<int,ll>dp;
int m[maxn],p[maxn],pt;
void init()
{
phi[]=;
int N=maxn-;
int k;
for(int i=;i<N;i++)
{
if(!m[i])
p[pt++]=m[i]=i,phi[i]=i-;
for(int j=;j<pt&&(k=p[j]*i)<N;j++)
{
m[k]=p[j];
if(m[i]==p[j])
{
phi[k]=phi[i]*p[j];
break;
}
else
phi[k]=phi[i]*(p[j]-);
}
}
for(int i=;i<=maxn-;i++)(phi[i]+=phi[i-])%=mod;
for(int i=;i<=maxn-;i++)phi[i]=(*phi[i]%mod-+mod)%mod;
}
ll dfs(int x)
{
if(x<=maxn-)return phi[x];
else if(dp.count(x))return dp[x];
ll ret=1ll*x*x%mod;
for(int i=,j;i<=x;i++)
{
j=x/(x/i);
ret-=dfs(x/i)*(j-i+)%mod;
(ret+=mod)%=mod;
i=j;
}
return dp[x]=ret;
}
int main()
{
int i,j;
init();
scanf("%d",&t);
while(t--)
{
ll ret=;
scanf("%d",&n);
for(i=,j;i<=n-;i++)
{
j=n/(n/i);
j=min(j,n-);
(ret+=(dfs(j)-dfs(i-)+mod)%mod*(n/i)%mod)%=mod;
i=j;
}
printf("%lld\n",ret*qpow(,mod-)%mod);
}
return ;
}

购买装备

发布时间: 2017年7月9日 18:17   最后更新: 2017年7月9日 21:05   时间限制: 1000ms   内存限制: 128M

描述

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

输入

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

输出

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

样例输入1

1

2 4

3 2

2 3

样例输出1

1 3

题解思路:先把装备按金钱从小到大排序然后求最多可以买多少个装备然后二分进行查找看最大能买最小的是多少即可

下面给出AC代码:

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int mx = 1e5+;
struct node{
int x,y;
bool operator<(node a)const{
if(y != a.y) return y<a.y;
return a.x > x;
}
}a[mx];
int n,m,num;
bool check(int x){
int ans = m;
int k = ;
for(int i = ; i < n; i++)
if(a[i].x >= x && a[i].y <= ans)
k++,ans-=a[i].y;
if(k >= num)
return true;
return false;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++)
scanf("%d%d",&a[i].x,&a[i].y);
sort(a,a+n);
int ans = m;
num = ;
for(int i = ; i < n; i++)
if(ans >= a[i].y)
num++,ans-=a[i].y;
else
break;
int l = ,r = ;
while(l < r){
int mid = (l+r+)/;
if(check(mid))
l = mid;
else
r = mid-;
}
printf("%d %d\n",num,l);
}
return ;
}

零件组装

发布时间: 2017年7月9日 20:20   最后更新: 2017年7月10日 21:12   时间限制: 1000ms   内存限制: 128M

描述

现有n个零件,小Y花费了很多时间来收集它们,现在他想把零件拼在一起,拼完就可以召唤神龙了。已知零件之间存在相邻的关系,拥有相邻关系的零件在最终的组装结果中就是相邻的,并且组装过程中每次只能通过相邻关系来组合零件。小Y每次可以选择两个零件(也可以是两个零件块,或一个零件与一个零件块)拼起来,成为一个零件块,但要求拼接时必须在两个零件块(或零件)之间存在相邻的零件。除此之外这些零件两两之间有类似于磁力的排斥关系,当将两个零件或者零件块拼接在一起的时候,会受到两边的零件间的排斥力,排斥力的大小=两边零件的相互排斥对数*单侧零件个数的最大值(拼接完成的零件组合体中的零件之间排斥不计)。现在已知零件间的相邻关系和排斥关系,小Y自然想知道如何拼接不费力,因此需要求出将这些零件组装起来的最优方案,使得所有步骤的排斥力之和最小。

输入

第一行有一个整数T表示数据组数。(T<=20)
接着有T组数据,每组数据第一行是整数n表示零件个数。
接着依此有两个n∗n的矩阵,都只由0和1构成。(2<=n<=14)
其中第一个矩阵表示零件两两之间的相邻关系,第i行第j列为1表示第i个零件与第j个零件相邻,
第二个矩阵表示零件两两之间的排斥关系,第i行第j列为1表示第i个零件与第j个零件排斥。
数据保证矩阵根据对角线对称,并保证通过零件的相邻关系可以最终拼接完成。

输出

每组输入一个整数表示拼接过程的最小排斥力之和。

样例输入1

1

4

0 0 1 1

0 0 1 0

1 1 0 0

1 0 0 0

0 1 0 1

1 0 1 1

0 1 0 0

1 1 0 0

样例输出1

6

下面给出AC代码:

 #include<stdio.h>
#include<iostream>
#include<string.h>
#include<string>
#include<ctype.h>
#include<math.h>
#include<set>
#include<map>
#include<vector>
#include<queue>
#include<bitset>
#include<algorithm>
#include<time.h>
using namespace std;
void fre() { freopen("c://test//input.in", "r", stdin); freopen("c://test//output.out", "w", stdout); }
#define MS(x, y) memset(x, y, sizeof(x))
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T1, class T2>inline void gmax(T1 &a, T2 b) { if (b > a)a = b; }
template <class T1, class T2>inline void gmin(T1 &a, T2 b) { if (b < a)a = b; }
const int N = , M = ( << ) + , Z = 1e9 + , inf = 0x3f3f3f3f;
template <class T1, class T2>inline void gadd(T1 &a, T2 b) { a = (a + b) % Z; }
int casenum, casei;
int con[N][N];
int cost[N][N];
int num2[M];
int f[M];
int n;
int num[M];
int a[M][];
int VAL(int x, int y)
{
bool isCon = ;
int val = ;
for (int i = ; i < num[x]; ++i)
{
int xx = a[x][i];
for (int j = ; j < num[y]; ++j)
{
int yy = a[y][j];
isCon |= con[xx][yy];
val += cost[xx][yy];
}
}
if (!isCon)return inf;
return val * max(num[x], num[y]);
}
int main()
{
for (int i = ; i <= ( << ); ++i)
{
num2[i] = num2[i >> ] + (i & );
for (int j = ; j < ; ++j)if (i >> j & )
{
a[i][num[i]++] = j;
}
}
scanf("%d", &casenum);
for (casei = ; casei <= casenum; ++casei)
{
scanf("%d", &n);
for (int i = ; i < n; ++i)
{
for (int j = ; j < n; ++j)
{
scanf("%d", &con[i][j]);
}
}
for (int i = ; i < n; ++i)
{
for (int j = ; j < n; ++j)
{
scanf("%d", &cost[i][j]);
}
}
MS(f, ); f[] = ;
int top = ( << n) - ;
for (int i = ; i <= top; ++i)
{
if (num2[i] == )
{
f[i] = ;
continue;
}
for (int j = i - & i; j; j = j - & i)
{
int k = i ^ j;
if(f[j] != inf && f[k] != inf)//不要忽视可能会溢出的特殊情况
gmin(f[i], f[j] + f[k] + VAL(j, k));
}
}
printf("%d\n", f[top]);
}
return ;
}

风力观测

发布时间: 2017年7月9日 20:20   最后更新: 2017年7月10日 21:12   时间限制: 1000ms   内存限制: 128M

描述

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

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

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

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

输入

第一行有一个整数T表示数据组数。(T<=10)
接着有T组数据,每组数据第一行是整数n和q,表示观测点个数和查询次数。
第二行有n个数a1,...,an,表示每个观测点的风力初始值。
接着有q行,表示q次操作,格式为:
1 L R X:表示对[L,R]线段上的正向风力同时增强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

题解思路:用线段树更新然后这个线段的历史最小历史最大值,如果用lazy思想更新是个难点

一开始把全部数值清0,然后用懒人思路如果该线段被更新那就求历史最低值和历史最高值也就是MIN和MAX,然后如何用lazy思想更新呢就下一段线段的历史最高等于上一段的历史最高加上下面一段还没有更新的lazy取两者最大,min的一样

下面给出AC代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<map>
#include<algorithm>
using namespace std;
#define mid (L+R)/2
#define ls 2*rt
#define rs 2*rt|1
#define lson ls,L,mid
#define rson rs,mid+1,R
const int mx = 1e5+;
typedef pair<int,int> P;
int MAX[mx<<],MIN[mx<<],lazy[mx<<];
int a[mx];
int n,m;
void push_down(int rt){
MAX[ls] = max(MAX[ls],MAX[rt]+lazy[ls]);
MIN[ls] = min(MIN[ls],MIN[rt]+lazy[ls]);
MIN[rs] = min(MIN[rs],MIN[rt]+lazy[rs]);
MAX[rs] = max(MAX[rs],MAX[rt]+lazy[rs]); //因为上面的lazy已经更新过了所以再给下面用一遍会重复所以不只要增加还未更新的即可
lazy[rs] += lazy[rt];
lazy[ls] += lazy[rt];
lazy[rt] = MAX[rt] = MIN[rt] = ;
}
void update(int rt,int L,int R,int l,int r,int v){
if(l<=L && R <= r){
lazy[rt]+=v;
MAX[rt] = max(MAX[rt],lazy[rt]); //这一段如果没有被更新过就是直接这样
MIN[rt] = min(MIN[rt],lazy[rt]); //如果这一段被更新过的话再更新的时候只要取历史的最高还是一样的结果
return;
}
if(MAX[rt]||MIN[rt]||lazy[rt]) //如果其中有一个不为0那就更新
push_down(rt);
if(l>mid) update(rson,l,r,v);
else if(r<=mid) update(lson,l,r,v);
else{
update(rson,mid+,r,v);
update(lson,l,mid,v);
}
}
P query(int rt,int L,int R,int x){
if(L == R && L == x)
return {MAX[rt],MIN[rt]};
if(MAX[rt]||MIN[rt]||lazy[rt])
push_down(rt);
if(x > mid) return query(rson,x);
else return query(lson,x);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
memset(MAX,,sizeof(MAX));
memset(MIN,,sizeof(MIN));
memset(lazy,,sizeof(lazy));
scanf("%d%d",&n,&m);
for(int i = ; i <= n; i++)
scanf("%d",&a[i]);
while(m--){
int casei;
scanf("%d",&casei);
if(casei == ){
int l,r,v;
scanf("%d%d%d",&l,&r,&v);
update(,,n,l,r,v);
}
else{
int x;
scanf("%d",&x);
P ans = query(,,n,x);
printf("%d\n",max(abs(a[x]+ans.first),abs(a[x]+ans.second)));
}
}
}
return ;
}

密码破解

发布时间: 2017年7月9日 20:20   最后更新: 2017年7月10日 21:12   时间限制: 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表示需要解密的数字序列长度。
第二行是需要解密的数字序列a1..an。
1<p,q,e<=108,p、q、e为质数且p!=q。
$0<=a_i<m$。($1<=i<=n$)< br=""
style="box-sizing: border-box;"> 保证解密的结果即原数列的值小于min(p,q)并大于等于0
1<=n<=100
保证m有且仅有两个不同的质因数p和q,并且一定存在一个题中描述的参数d使得解密公式能够无损解密出所有0~min(p,q)−1范围之间的数字。</m$。($1<=i<=n$)<>

输出

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

样例输入1

1

5 19 29 3

335 440 514

样例输出1

65 67 77

提示

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

注意m和ai的大小可能超过int的范围

思路:

数学分析得出一个等式:“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

要求最小的正整数d,那么可以用扩展欧几里得求的。

然后对每个密文跑超级快速幂就能得到答案

详细的数学推导过程:

因为p,q为素数,那么可知:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

可以推出

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

再由题目给出的公式:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

将以第一个式子里的b的同余式代入第二个式子:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

那么结合一下:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

可以推出:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

因为:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

所以:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

转换一下:

“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛题解&&源码【A,水,B,水,C,水,D,快速幂,E,优先队列,F,暴力,G,贪心+排序,H,STL乱搞,I,尼姆博弈,J,差分dp,K,二分+排序,L,矩阵快速幂,M,线段树区间更新+Lazy思想,N,超级快速幂+扩展欧里几德,O,BFS】

然后就可以直接用扩展欧几里得求解最小的正整数 d 了。

需要注意的是解密的时候由于m太大,直接跑快速幂可能会出现溢出,需要用超级快速幂。

下面给出AC代码:

 #include<bits/stdc++.h>
#define ULL unsigned long long
using namespace std;
void ex_gcd(long long a,long long b,long long &x,long long &y){
if(!b){x=;y=;}
else {
ex_gcd(b,a%b,y,x);
y -= a/b * x;
}
}
inline ULL fast_mul(ULL x,ULL y,ULL mod){
ULL res = ;
while(y){
if(y&){
res += x;
while(res>=mod){
res -= mod;
}
}
y>>=;
x<<=;
while(x>=mod){
x -= mod;
}
}return res;
}
inline ULL fast_pow(ULL x,long long y,ULL mod){
ULL res = ;
while(y){
if(y&){
res = fast_mul(res,x,mod);
}y>>=;
x = fast_mul(x,x,mod);
}return res;
}
long long A[];
int main()
{
int T;
scanf("%d",&T);
while(T--){
long long e , p ,q ;
int n;
scanf("%lld%lld%lld%d",&e,&p,&q,&n);
unsigned long long m = q * p;
unsigned long long lcm = (q-)*(p-)/__gcd(q-,p-);
long long d , k ;
ex_gcd(e,lcm,d,k);
while(d<)d+=lcm;
for(int i=;i<n;i++){
unsigned long long x ;
scanf("%llu",&x);
printf("%llu%c",fast_pow(x,d,m),i<n-?' ':'\n');
}
}
}

随机传送迷宫

发布时间: 2017年7月9日 20:20   最后更新: 2017年7月10日 21:13   时间限制: 1000ms   内存限制: 128M

描述

小Y做了一个随机迷宫生成器,生成的迷宫可以用n∗m的矩阵来表示,人物可以从迷宫的起点开始,每个单位时间选择往上下左右走一步,但不能走到不能通行的格子或者走出矩阵外。在迷宫中会有不定数量的传送门,其中传送门入口与传送门出口一一对应,如果人物处在传送门入口处,可以用一个单位的时间使用这个传送门入口到达一个传送门出口处,但是在尝试之前你并不知道哪一个传送门入口对应哪一个出口,因此小Y想知道用什么样的策略走才能在最坏情况下花费最少时间到达迷宫的出口。传送门只能单向通行,但是可以重复使用,由于传送门入口出口一一对应,因此重复使用传送门会到达同一个传送门出口。

输入

第一行有一个整数T表示数据组数。(T<=50)
接着有T组数据,每组数据第一行有两个整数n、m。(2<=n,m<=100)
接着有n行,m列的迷宫矩阵。
其中字符'S','T'分别表示唯一的迷宫起点和终点,
字符'#'表示无法通行的障碍,
字符'.'表示空地,
字符'i','o'分别表示传送门入口和出口。(传送门入口与出口数量相同,并且数量都不超过5)

输出

对于每组数据输出一行,表示花费的最少单位时间,如果不存在一定能走到出口的方案,则输出-1。

样例输入1

3
4 4
Siii
..oo
####
T..o
4 4
S..i
i###
#.#o
T.o#
5 4
o.Si
###i
.oo#
i###
i#oT

样例输出1

9
-1
16

题解思路:在队友的指导下碰到第一个传送点传送后肯定最优因为你传送完第一个第二个就不用传了递归下去还是跟之前一样的情况然后再未知的情况下有传送点那么就是传送点中最恶劣情况下的最少走的步数然后如果不用传送阵能走到的话就是min(不用,用)

然后就是BFS注意因为可能重复走来走去所以要设最大步数是1e6

下面给出AC代码:

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
int mark[];
char map[][];
int num[][];
struct pos{
int x,y,t;
}a[];
int x_move[] = {,,,-};
int y_move[] = {-,,,};
int len1,len,n,m;
int sx,sy,ex,ey,ans;
bool check(int x,int y,bool vis[][]){
return x >= && y >= && x < n && y < m && !vis[x][y] && map[x][y] !='#';
}
int bfs(int sx,int sy){
pos ft,pt;
ft.x = sx;
ft.y = sy;
ft.t = ;
bool vis[][];
memset(vis,,sizeof(vis));
vis[sx][sy] = ;
queue<pos>q;
q.push(ft);
int flag = ;
int ans = ;
while(!q.empty()){
ft = q.front();
if(ft.x == ex && ft.y == ey)
return min(ans,ft.t);
q.pop();
for(int i = ;i < ; i++){
pt.x = ft.x+x_move[i];
pt.y = ft.y+y_move[i];
if(check(pt.x,pt.y,vis)){
pt.t = ft.t+;
if(map[pt.x][pt.y] == 'i'&&!flag)
for(int j = ; j < len; j++)
if(!mark[j]){
mark[j] = ;
map[pt.x][pt.y] = '.';
// num[pt.x][pt.y] = 1;
if(!flag) ans = pt.t+bfs(a[j].x,a[j].y)+,flag = ;
else ans = max(ans,pt.t+bfs(a[j].x,a[j].y)+);
//cout<<ans<<endl;
mark[j] = ;
map[pt.x][pt.y] = 'i';
}
vis[pt.x][pt.y] = ;
q.push(pt);
}
}
}
return ans;
}
int main(){
int t;
scanf("%d",&t);
while(t--){
ans = len = ;
memset(mark,,sizeof(mark));
memset(num,,sizeof(num));
scanf("%d%d",&n,&m);
for(int i = ; i < n; i++){
scanf("%s",map[i]);
for(int j = ; j < m; j++)
if(map[i][j] == 'o'){
a[len].x = i;
a[len].y = j;
len++;
}
else if(map[i][j] == 'S')
sx = i,sy = j;
else if(map[i][j] == 'T')
ex = i,ey = j;
}
// cout<<len<<endl;
ans = bfs(sx,sy);
printf("%d\n",ans>?-:ans);
}
return ;
}