提高组
T1 假期计划
题目大意:有 n n n个点, m m m条边,每个点都有一个权值。现在要从第一个点开始,走到四个点在走回家,每走一次可以经过 k k k个点。
设 f [ i ] [ 0 / 1 / 2 ] f[i][0/1/2] f[i][0/1/2]表示从第一个点走两次到第 i i i个点的权值和的最大值、次大值、次次大值,用 b f s bfs bfs求出 f f f值,最后求解即可。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,k,x,y,tot=0,d[20005],l[20005],r[20005],z[3005],g[3005][3],v[2505][2505];
long long ans,a[3005];
queue<int>q;
void add(int xx,int yy){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;
}
void bfs(int v0){
q.push(v0);
while(!q.empty()){
int u=q.front();q.pop();
for(int i=r[u];i;i=l[i]){
if(z[d[i]]) continue;
z[d[i]]=1;v[v0][d[i]]=v[v0][u]+1;
q.push(d[i]);
}
}
}
int main()
{
// freopen("holiday.in","r",stdin);
// freopen("holiday.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);++k;
for(int i=2;i<=n;i++) scanf("%lld",&a[i]);
a[0]=a[1]=-2e18;
for(int i=1;i<=m;i++){
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) v[i][j]=k+1;
v[i][i]=0;
memset(z,0,sizeof(z));
z[i]=1;
bfs(i);
v[i][i]=v[i][1]=k+1;
}
for(int i=2;i<=n;i++){
if(v[1][i]>k) continue;
for(int j=2;j<=n;j++){
if(v[i][j]>k) continue;
if(a[i]>a[g[j][0]]){
g[j][2]=g[j][1];g[j][1]=g[j][0];g[j][0]=i;
}
else if(a[i]>a[g[j][1]]){
g[j][2]=g[j][1];g[j][1]=i;
}
else if(a[i]>a[g[j][2]]){
g[j][2]=i;
}
}
}
for(int i=2;i<=n;i++){
for(int j=2;j<=n;j++){
if(v[i][j]>k) continue;
for(int v1=0;v1<=2;v1++){
for(int v2=0;v2<=2;v2++){
if(i==g[j][v2]||g[i][v1]==j||g[i][v1]==g[j][v2]) continue;
ans=max(ans,a[i]+a[j]+a[g[i][v1]]+a[g[j][v2]]);
}
}
}
}
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}
T2 策略游戏
题目大意:给一个长度为 n n n的数组 A A A和一个长度为 m m m的数组 B B B, C i , j = A i × B j C_{i,j}=A_i\times B_{j} Ci,j=Ai×Bj。有 q q q轮游戏,每轮给定一个范围,小L先选一个 i i i,小 Q Q Q后选一个 j j j。小 L L L想使 C i , j C_{i,j} Ci,j尽可能大,小Q想使 C i , j C_{i,j} Ci,j尽可能小。两人都会使用最优策略,求每轮游戏中最后的 C i , j C_{i,j} Ci,j的值
分类讨论:
1.若在范围中选择的
A
i
A_i
Ai大于
0
0
0,则
B
i
B_i
Bi为范围中的最小值
- 若 B i B_i Bi最小值大于 0 0 0,则 A i A_i Ai取大于 0 0 0的最大值
- 若 B i B_i Bi最小值小于 0 0 0,则 A i A_i Ai取大于 0 0 0的最小值
- 若 B i B_i Bi最小值等于 0 0 0,则 A i A_i Ai取大于 0 0 0的任意值
2.若在范围中选择的 A i A_i Ai小于 0 0 0,则 B i B_i Bi为范围中的最大值
- 若 B i B_i Bi最大值大于 0 0 0,则 A i A_i Ai取小于 0 0 0的最大值
- 若 B i B_i Bi最大值小于 0 0 0,则 A i A_i Ai取小于 0 0 0的最小值
- 若 B i B_i Bi最大值等于 0 0 0,则 A i A_i Ai取小于 0 0 0的任意值
3.若在范围中选择的 A i A_i Ai等于 0 0 0,则 C i , j C_{i,j} Ci,j为 0 0 0
在三种情况中取最小值就是答案
我用的是倍增,用线段树也可以过,时间复杂度为 O ( q log n ) O(q\log n) O(qlogn)
code
#include<bits/stdc++.h>
using namespace std;
int n,m,q,l1,r1,l2,r2,lg[100005],ze[100005];
long long mx,mn,ans,a[100005],b[100005],mxb[100005][20],mnb[100005][20];
long long mx1[100005][20],mx2[100005][20],mn1[100005][20],mn2[100005][20];
long long fmx1(int l,int r){
if(l==r) return mx1[l][0];
return max(mx1[l][lg[r-l+1]],mx1[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
}
long long fmn1(int l,int r){
if(l==r) return mn1[l][0];
return min(mn1[l][lg[r-l+1]],mn1[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
}
long long fmx2(int l,int r){
if(l==r) return mx2[l][0];
return max(mx2[l][lg[r-l+1]],mx2[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
}
long long fmn2(int l,int r){
if(l==r) return mn2[l][0];
return min(mn2[l][lg[r-l+1]],mn2[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
}
int main()
{
// freopen("game.in","r",stdin);
// freopen("game.out","w",stdout);
scanf("%d%d%d",&n,&m,&q);lg[0]=-1;
for(int i=1;i<=max(n,m);i++) lg[i]=lg[i/2]+1;
for(int i=1;i<=n;i++){
scanf("%lld",&a[i]);
if(a[i]>0){
mx1[i][0]=mn1[i][0]=a[i];
mx2[i][0]=-1e9-1;mn2[i][0]=0;
}
else if(a[i]<0){
mx2[i][0]=mn2[i][0]=a[i];
mx1[i][0]=0;mn1[i][0]=1e9+1;
}
ze[i]=ze[i-1]+(a[i]==0);
}
for(int i=1;i<=m;i++){
scanf("%lld",&b[i]);
mxb[i][0]=mnb[i][0]=b[i];
}
for(int i=1;i<=19;i++){
for(int j=1;j+(1<<i)-1<=n;j++){
mx1[j][i]=max(mx1[j][i-1],mx1[j+(1<<i-1)][i-1]);
mx2[j][i]=max(mx2[j][i-1],mx2[j+(1<<i-1)][i-1]);
mn1[j][i]=min(mn1[j][i-1],mn1[j+(1<<i-1)][i-1]);
mn2[j][i]=min(mn2[j][i-1],mn2[j+(1<<i-1)][i-1]);
}
for(int j=1;j+(1<<i)-1<=m;j++){
mxb[j][i]=max(mxb[j][i-1],mxb[j+(1<<i-1)][i-1]);
mnb[j][i]=min(mnb[j][i-1],mnb[j+(1<<i-1)][i-1]);
}
}
while(q--){
ans=-2e18;
scanf("%d%d%d%d",&l1,&r1,&l2,&r2);
if(l2==r2) mx=mn=b[l2];
else{
mx=max(mxb[l2][lg[r2-l2+1]],mxb[r2-(1<<lg[r2-l2+1])+1][lg[r2-l2+1]]);
mn=min(mnb[l2][lg[r2-l2+1]],mnb[r2-(1<<lg[r2-l2+1])+1][lg[r2-l2+1]]);
}
if(fmx1(l1,r1)>0){
if(mn<0) ans=max(ans,mn*fmn1(l1,r1));
else ans=max(ans,mn*fmx1(l1,r1));
}
if(fmn2(l1,r1)<0){
if(mx<0) ans=max(ans,mx*fmn2(l1,r1));
else ans=max(ans,mx*fmx2(l1,r1));
}
if(ze[r1]-ze[l1-1]>0) ans=max(ans,0ll);
printf("%lld\n",ans);
}
fclose(stdin);
fclose(stdout);
return 0;
}
T3 星战
没有AC
T4 数据传输
没有AC
提高组总结
没有拿完应得的分,分数230。
普及组
T1 乘方
题目大意:给出 a a a和 b b b,如果 a b a^b ab小于等于 1 0 9 10^9 109,则输出 a b a^b ab,否则输出 − 1 -1 −1
快速幂,每做一次判断一次,时间复杂度为 O ( log n ) O(\log n) O(logn)
直接乘也行,特判 1 1 1之后,因为 2 30 > 1 0 9 2^{30}>10^9 230>109,所以最多只会乘不超过 30 30 30次。
我用的是快速幂。
code
#include<bits/stdc++.h>
using namespace std;
long long a,b,x,s,inf=1e9;
int main()
{
// freopen("pow.in","r",stdin);
// freopen("pow.out","w",stdout);
scanf("%lld%lld",&a,&b);
x=a;s=1;
for(;b;b>>=1){
if(x>inf||s>inf){
printf("-1");
return 0;
}
if(b&1) s=s*x;
x=x*x;
}
if(s>inf) s=-1;
printf("%lld",s);
fclose(stdin);
fclose(stdout);
return 0;
}
T2 解密
题目大意: k k k次询问,每次给出 n , e , d n,e,d n,e,d,求正整数 p , q p,q p,q,使 n = p × q , e × d = ( p − 1 ) ( q − 1 ) + 1 n=p\times q,e\times d=(p-1)(q-1)+1 n=p×q,e×d=(p−1)(q−1)+1
p
×
q
=
n
p\times q=n
p×q=n
p
+
q
=
p
×
q
−
(
p
×
q
−
p
−
q
+
2
)
+
2
=
n
−
e
×
d
+
2
p+q=p\times q-(p\times q-p-q+2)+2=n-e\times d+2
p+q=p×q−(p×q−p−q+2)+2=n−e×d+2
p × q , p + q p\times q,p+q p×q,p+q已知,那么 p , q p,q p,q就是方程 x 2 − ( p + q ) x + p × q = 0 x^2-(p+q)x+p\times q=0 x2−(p+q)x+p×q=0的两个根,用求根公式即可求出,最后判断是否为正整数即可。
求根公式: x 1 = − b + b 2 − 4 a c 2 a , x 2 = − b − b 2 − 4 a c 2 a x_1=\dfrac{-b+\sqrt{b^2-4ac}}{2a},x_2=\dfrac{-b-\sqrt{b^2-4ac}}{2a} x1=2a−b+b2−4ac,x2=2a−b−b2−4ac
code
#include<bits/stdc++.h>
using namespace std;
int t;
long long a,b,c,d,d1,v1,v2,p,q;
int main()
{
// freopen("decode.in","r",stdin);
// freopen("decode.out","w",stdout);
scanf("%d",&t);
while(t--){
scanf("%lld%lld%lld",&a,&b,&c);
v1=a-b*c+2;
v2=a;
d=v1*v1-4*v2;
if(d<0){
printf("NO\n");
continue;
}
d1=sqrt(d);
if(d1*d1!=d){
printf("NO\n");
continue;
}
d=d1;
if((v1+d)%2==1){
printf("NO\n");
continue;
}
p=(v1-d)/2;q=(v1+d)/2;
if(p<=0){
printf("NO\n");
continue;
}
printf("%lld %lld\n",p,q);
}
fclose(stdin);
fclose(stdout);
return 0;
}
T3 逻辑表达式
题目大意:给出一个逻辑表达式,其中包含左右括号,&,|,0,1。
括号优先级最大,&优先级第二,|优先级最小。当计算a&b时,若a为0,则不计算b;当计算a|b时,若a为1,则不计算b。称以上两种情况为短路。求逻辑表达式的值,以及&短路的次数和|短路的次数。如果某处短路包含在更外层的短路,则这处短路不计。
设 g t ( l , r ) gt(l,r) gt(l,r)表示求逻辑表达式中 [ l , r ] [l,r] [l,r]部分&和|分别短路的次数,返回该部分的值。那么在处理 [ l , r ] [l,r] [l,r]的时候:
- 如果这一段有|且不被括号包含,则处理|之前的,如果=1,则|短路一次
- 否则如果这一段有&且不被括号包含,则处理&之前的,如果=0,则&短路一次
- 如果这一段有|或&但都被括号包含,那 l , r l,r l,r的位置一定是左右括号, g t ( l + 1 , r − 1 ) gt(l+1,r-1) gt(l+1,r−1)
- 如果这一段没有|也没有&,那么 l = = r l==r l==r,这个位置一定是0或1,返回相应值即可
code
#include<bits/stdc++.h>
using namespace std;
int n,v1,v2,ans,h[1000005],v[1000005],dep[1000005];
int vt[1000005],lt[1000005],vk[1000005],lk[1000005];
char s[1000005];
int gt(int l,int r,int d){
if(l==r) return s[l]-'0';
if(s[l]=='('&&s[r]==')'&&v[l]==r) return gt(l+1,r-1,d+1);
if(lt[r]>=l){
int i=lt[r];
if(gt(l,i-1,d)){
++v2;return 1;
}
else return gt(i+1,r,d);
}
int i=lk[r];
if(!gt(l,i-1,d)){
++v1;return 0;
}
else return gt(i+1,r,d);
}
int main()
{
// freopen("expr.in","r",stdin);
// freopen("expr.out","w",stdout);
scanf("%s",s+1);
n=strlen(s+1);
for(int i=1,k=0;i<=n;i++){
if(s[i]=='(') h[++k]=i;
else if(s[i]==')'){
v[h[k]]=i;--k;
}
dep[i]=k;
if(s[i]=='|') vt[k]=i;
else if(s[i]=='&') vk[k]=i;
lt[i]=vt[k];lk[i]=vk[k];
}
ans=gt(1,n,0);
printf("%d\n%d %d",ans,v1,v2);
fclose(stdin);
fclose(stdout);
return 0;
}
T4 上升点列
题目大意:给出平面上的 n n n个点和一个 k k k, k k k表示可以在平面上添加 k k k个点。添加后要从这 n + k n+k n+k个点中选出若干个整数点并组成一个序列,使序列中任意相邻两点间的欧几里得距离恰好为 1 1 1且横坐标、纵坐标值均单调不减。
首先,将点按 x x x值从小到大排序, x x x值相等则按 y y y值从小到大排序。
然后,我们设 f [ i ] [ j ] f[i][j] f[i][j]表示以第 i i i个点为终点,已经放了 j j j个点的最长序列的长度。每两个点连一条边,共连 n 2 n^2 n2条边。依次遍历每个点,更新 f [ i ] [ j ] f[i][j] f[i][j]。每个点更新的时间复杂度为 O ( n k ) O(nk) O(nk),总时间复杂度为 O ( n 2 k ) O(n^2k) O(n2k)
code
#include<bits/stdc++.h>
using namespace std;
int n,k,tot=0,ans=0,d[500005],l[500005],r[500005],v[500005],ct[505],f[505][105];
queue<int>q;
struct node{
int x,y;
}w[505];
void add(int xx,int yy,int zz){
l[++tot]=r[xx];d[tot]=yy;r[xx]=tot;v[tot]=zz;++ct[yy];
}
int main()
{
// freopen("point.in","r",stdin);
// freopen("point.out","w",stdout);
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++){
scanf("%d%d",&w[i].x,&w[i].y);
f[i][0]=1;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j&&w[i].x<=w[j].x&&w[i].y<=w[j].y){
add(i,j,w[j].x-w[i].x+w[j].y-w[i].y-1);
}
}
}
for(int i=1;i<=n;i++){
if(!ct[i]) q.push(i);
}
while(!q.empty()){
int u=q.front();q.pop();
for(int i=r[u];i;i=l[i]){
for(int j=v[i];j<=k;j++){
f[d[i]][j]=max(f[d[i]][j],f[u][j-v[i]]+1);
}
--ct[d[i]];
if(ct[d[i]]==0) q.push(d[i]);
}
}
for(int i=1;i<=n;i++){
for(int j=0;j<=k;j++){
ans=max(ans,f[i][j]);
}
}
printf("%d",ans+k);
fclose(stdin);
fclose(stdout);
return 0;
}
普及组总结
普及组考得还不错,分数400分。