21级爪哇程序设计新生赛(一)
A 小爪的金铲铲(签到)
怎么说呢,前面几段废话是林建诚师兄费尽心思找的,纯粹是用来迷惑大家的。真正有用的只有最后一段话。 那我们可以根据题意写几个例子,可以发现答案都是3的n-1次方。找到规律就可以直接写出来程序了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int main()
{
int n;
while(cin>>n)
{
long long sum=1;
n--;
for (int i=1;i<=n;i++) sum*=3;
cout<<sum<<endl;
}
return 0;
}
B 小爪查单词(字符串)
因为不分大小写,输入之后为了方便可以将所有的大写都变成小写,将每个单词拎出来,然后一个一个单词的判断就行了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
int main()
{
string s1,s2,s3="";
getline(cin,s1);
getline(cin,s2);
int len=s1.length();
for (int i=0;i<len;i++)
if (s1[i]<='Z' && s1[i]>='A') s1[i]+=-'A'+'a';
int res=0,f=-1;
for (int j=0;j<s2.length();j++)
{
char c=s2[j];
if (c<='Z' && c>='A') c+=-'A'+'a';
if (c!=' ') s3+=c;
else
{
if (s3.length()==len)
{
bool flag=true;
for (int i=0;i<len;i++)
if (s1[i]!=s3[i])
{
flag=false;
break;
}
if (flag)
{
if (res==0) f=j-s3.length();
res++;
}
}
s3="";
}
}
if (s3.length()==len)
{
bool flag=true;
for (int i=0;i<len;i++)
if (s1[i]!=s3[i])
{
flag=false;
break;
}
if (flag)
{
if (res==0) f=s2.length()-s3.length();
res++;
}
}
if (res!=0) cout<<res<<" ";
cout<<f;
return 0;
}
C 小爪的数学题(数学)
纯纯的计算题,算出平均数然后求方差即可。同时统计出最大和最小值。注意细节就行,没啥好说的
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
double a[1005];
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
double s=0;
for (int i=0;i<n;i++)
{
cin>>a[i];
s+=a[i];
}
if (n==1)
{
cout<<0<<" "<<0<<endl;
continue;
}
s/=(double)n;
double maxa=a[0],mina=a[0];
double res=(a[0]-s)*(a[0]-s);
for (int i=1;i<n;i++)
{
maxa=max(a[i],maxa);
mina=min(a[i],mina);
res+=(a[i]-s)*(a[i]-s);
}
res/=(double)n;
printf("%.lf %.3lf\n",maxa-mina,res);
}
return 0;
}
D 小爪的查询(easy version)(前缀和)
前缀和思想,前缀和是指某序列的前n项和,可以把它理解为数学上的数列的前n项和,a0=0,Sn=a1+a2+…+an,a2+a3+a4=S4-S1,a1+a2+a3=S3-S0
#include <iostream>
using namespace std;
const int N=1e5+5;
int n, m;
long long a[N],s[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
while (m--)
{
int l,r;
scanf("%d%d", &l,&r);
printf("%lld\n",s[r]-s[l-1]);
}
return 0;
}
E 小爪的查询(hard version)(st表模板)
倍增,RMQ算法模板题
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
int f[100001][40],x,len;
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
int a;
cin>>a;
f[i][0]=a;
}
int p=(int)(log(n)/log(2));
for(int j=1;j<=p;j++)
for (int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++)
{
int l,r;
scanf("%d%d",&l,&r);
p=(int)(log(r-l+1)/log(2));
printf("%d\n",max(f[l][p],f[r-(1<<p)+1][p]));
}
return 0;
}
F 小爪的工资管理(线段树)
也是林建诚师兄找的题
线段树
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<stack>
using namespace std;
const int maxn = 4e5 + 5;
const int maxn2 = 1e5 + 5;
int p;
struct node {
long long int val = 0;
long long int add = 0;//记录要加的数字
long long int add2 = 1;//记录要乘的数字
};
node counts[maxn];
int numbers[maxn2];
void pushdown(int l, int r, int n)
{
int mid = (l + r) / 2;
int left = 2 * n + 1;
int right = 2 * n + 2;
counts[left].val = (counts[left].val * counts[n].add2 + (mid - l + 1) * counts[n].add) % p;//优先计算乘法counts[root*2].val=(counts[root*2].val*counts[root].mul+counts[root].add*(本区间长度))%p
counts[right].val = (counts[right].val * counts[n].add2 + (r - mid) * counts[n].add) % p;
counts[left].add2 = (counts[left].add2 * counts[n].add2) % p;
counts[right].add2 = (counts[right].add2 * counts[n].add2) % p;
counts[left].add = (counts[left].add * counts[n].add2 + counts[n].add) % p;
counts[right].add = (counts[right].add * counts[n].add2 + counts[n].add) % p;
counts[n].add2 = 1;
counts[n].add = 0;
return;
}
void build(int s, int e, int n)
{
if (s == e)
{
counts[n].val = numbers[s];
return;
}
int mid = (s + e) / 2;
int left = n * 2 + 1;
int right = n * 2 + 2;
build(s, mid, left);//构建左子树
build(mid + 1, e, right);//构建右子树
counts[n].val = (counts[left].val + counts[right].val) % p;//左区间+右区间
}
void update_add(int s, int e, int L, int R, int v, int n)
{
if (L <= s && R >= e)
{
counts[n].val = (counts[n].val + (e - s + 1) * v) % p;
counts[n].add = (counts[n].add + v) % p;
return;
}
int mid = (s + e) / 2;
int left = 2 * n + 1;
int right = 2 * n + 2;
pushdown(s, e, n);
if (L <= mid)
{
update_add(s, mid, L, R, v, left);//更新左区间
}
if (R > mid)
{
update_add(mid + 1, e, L, R, v, right);//更新右区间
}
counts[n].val = (counts[left].val + counts[right].val) % p;
}
void update_mu(int s, int e, int L, int R, int v, int n)
{
if (L <= s && R >= e)
{
counts[n].val = (counts[n].val * v) % p;
counts[n].add2 = (counts[n].add2 * v) % p;
counts[n].add = (counts[n].add * v) % p;
return;
}
int mid = (s + e) / 2;
int left = 2 * n + 1;
int right = 2 * n + 2;
pushdown(s, e, n);//懒标记
if (L <= mid)
{
update_mu(s, mid, L, R, v, left);
}
if (R > mid)
{
update_mu(mid + 1, e, L, R, v, right);
}
counts[n].val = (counts[left].val + counts[right].val) % p;
}
long long int query(int s, int e, int L, int R, int n)
{
if (L <= s && R >= e)
{
return counts[n].val;
}
int mid = (s + e) / 2;
int left = 2 * n + 1;
int right = 2 * n + 2;
pushdown(s, e, n);
long long int sum = 0;
if (L <= mid)
sum = (sum + query(s, mid, L, R, left)) % p;
if (R > mid)
sum = (sum + query(mid + 1, e, L, R, right)) % p;
return sum % p;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
cin >> n >> m >> p;
for (int i = 0; i < n; i++)
cin >> numbers[i];
build(0, n - 1, 0);
while (m--)
{
int com;
cin >> com;
if (com == 1)
{
int x, y, k;
cin >> x >> y >> k;
x--, y--;
update_mu(0, n - 1, x, y, k, 0);
}
else if (com == 2)
{
int x, y, k;
cin >> x >> y >> k;
x--, y--;
update_add(0, n - 1, x, y, k, 0);
}
else
{
int x, y;
cin >> x >> y;
x--, y--;
cout << query(0, n - 1, x, y, 0) << endl;
}
}
}
G 小爪打怪(思维)
从前往后计算每次删除操作后的操作数,再从后往前计算从当前位置往后删需要的操作数,然后枚举使用魔法后的操作数,求最小即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
long long a[1000005],b[1000005];
long long p[1000005],q[1000005];
int main()
{
int n;
cin>>n;
for (int i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i];
}
for (int i=1;i<=n;i++)
{
if (a[i]>0)
{
p[i]=p[i-1]+a[i];
a[i+1]-=a[i];
a[i+2]-=a[i];
}else p[i]=p[i-1];
}
for (int i=n;i>=1;i--)
{
if (b[i]>0)
{
q[i]=q[i+1]+b[i];
if (i-1>=0)b[i-1]-=b[i];
if (i-2>=0)b[i-2]-=b[i];
}else q[i]=q[i+1];
}
long long res=p[n];
for (int i=1;i<=n;i++) res=min(res,p[i-1]+q[i+2]);
cout<<res<<endl;
return 0;
}
H 小爪的矩形(签到题)
签到题,如果有两根一样就看看第三根能不能分成两根一样的,如果不能组成一个矩形,那么就看看其中一根能否分成另外两根
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
int main()
{
int a,b,c,T;
cin>>T;
while(T--)
{
cin>>a>>b>>c;
if ((a==b && c%2==0) || (a==c&& b%2==0) || (b==c && a%2==0)) cout<<"YES"<<endl;
else if (a==b+c || b==a+c || c==a+b) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}
I 小爪的出行(思维)
只需要将总时间加起来,然后看看能否整除2即可。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cstdlib>
using namespace std;
int main()
{
long long a,b,c,T;
cin>>T;
while(T--)
{
cin>>a>>b>>c;
long long t=a+b*2+c*3;
t%=2;
cout<<t<<endl;
}
return 0;
}
J 小爪的庆生(动态规划)
动态规划
将删除路标变成建设路标,这样删除最多k个路标就变成了建设最少n-k个路标。
dp[i][j]表示为到第i个坐标时有j个路牌的时间最小值。而状态转移方程if (j-1<=m) dp[i][j]=min(dp[i][j],dp[m][j-1]+(w[i]-w[m])*v[m]);为枚举从1到i个路标数j然后再枚举从1到i-1的坐标m,然后看看是坐标为m有j-1个路标时的最小值加上从m到i的时间与dp[i][j]谁更小,记录dp[i][j]的最小值。最后只需看看坐标为l路标数为n到n-k里最小值是多少即可。
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
typedef long long ll;
typedef pair<int ,int> PII;
const int INF=0x3f3f3f35;
const int MOD=1e9+7;
const int N=505;
int dp[N][N];
int w[N],v[N];
int main()
{
int n,l,k;
cin>>n>>l>>k;
for (int i=1;i<=n;i++) cin>>w[i];
for (int i=1;i<=n;i++) cin>>v[i];
for (int i=0;i<=n+1;i++)
for (int j=0;j<=n+1;j++) dp[i][j]=INF;
dp[1][1]=0;
w[++n]=l;
for (int i=2;i<=n;i++)
for (int j=1;j<=i;j++)
for (int m=1;m<i;m++)
if (j-1<=m) dp[i][j]=min(dp[i][j],dp[m][j-1]+(w[i]-w[m])*v[m]);
int res=INF;
for (int i=0;i<=k;i++) res=min(res,dp[n][n-i]);
cout<<res<<endl;
return 0;
}
K 小爪去买瓜(动态规划)
dp[i]表示总重量为i时的方案数
状态转移方程:
if (j>=a[i])dp[j]=(dp[j]+dp[j-a[i]])%mod;
dp[j]=(dp[j]+dp[j-a[i]/2])%mod;
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <queue>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<int ,int> PII;
const int INF=0x3f3f3f;
const int mod=1e9+7;
const int N=1e3+5;
int a[N];
ll dp[N];
bool f[N];
int main()
{
int n,m;
cin>>n>>m;
for (int i=1;i<=n;i++) cin>>a[i];
sort(a+1,a+1+n);
dp[0]=1;
for (int i=1;i<=n;i++)
{
for (int j=m;j>=a[i]/2;j--)
{
if (j>=a[i])dp[j]=(dp[j]+dp[j-a[i]])%mod;
dp[j]=(dp[j]+dp[j-a[i]/2])%mod;
}
}
for (int i=1;i<=m;i++) cout<<dp[i]<<" ";
cout<<endl;
return 0;
}
L 小爪的素数 (easy version)(判断素数)
直接根据定义判断素数
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
#include <string>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
string flag="YES";
for (int i=2;i*i<=n;i++)
if (n%i==0)
{
flag="NO";
break;
}
cout<<flag<<endl;
}
return 0;
}
小爪的素数(hard version)(快速判断素数)
数据较大就无法用定义的方法来解决问题了
快速判断素数
原理
大于等于5的素数与6的倍数相邻
证明
所有自然数可以用集合A = { 6n, 6n+1, 6n+2, 6n+3, 6n+4, 6n+5 }表示,其中 n >= 0,显然,子集B = {6n, 6n+2, 6n+3, 6n+4}内的元素都不是素数,所以只有6n+1和6n+5可能是素数,素数一定可以用6n+1和6n+5其中的一个形式表示,即大于等于5的素数与6的倍数相邻 。
因子判断
上面说到大于或等于5的素数一定可以用6n+1或者6n+5来表示,在判断一个数num是否是素数时,需要判断num是否有除1和自身之外的因子。这时只需要判断x是否是num的因子,其中1 < x <= int(sqrt(num)),而x的范围又可以用集合{ 6i, 6i+1, 6i+2, 6i+3, 6i+4, 6i+5 }表示。当num与6的倍数相邻时,num才可能是素数,因为num(奇数)和{6i, 6i+2, 6i+3, 6i+4}(偶数)互素,也就是{6i, 6i+2, 6i+3, 6i+4}不可能是num的因子,此时只需判断x = 6i+1和x = 6i+5是否是num的因子即可,如果都不是,则num是素数。
#include <iostream>
#include <cmath>
#include <cstdio>
#include <Windows.h>
using namespace std;
const int N=1e5+5;
int n, m;
long long a[N],s[N];
int isprime(const long long num)
{
unsigned long long i, half;
if (num <= 1)
return 0;
if (num == 2 || num == 3)
return 1;
// 不和6的倍数相邻的数不是素数
else if (num % 6 != 1 && num % 6 != 5)
return 0;
half = (unsigned int)sqrt((double)num);
for (i = 5; i <= half; i += 6)
{
if (num % i == 0 || num % (i + 2) == 0)
return 0;
}
return 1;
}
int main()
{
int T;
cin>>T;
while(T--)
{
long long n;
cin>>n;
if (isprime(n)) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
return 0;
}