提高组
T1 假期计划
题目大意:有nnn个点,mmm条边,每个点都有一个权值。现在要从第一个点开始,走到四个点在走回家,每走一次可以经过kkk个点。
设f[i][0/1/2]f[i][0/1/2]f[i][0/1/2]表示从第一个点走两次到第iii个点的权值和的最大值、次大值、次次大值,用bfsbfsbfs求出fff值,最后求解即可。
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 策略游戏
题目大意:给一个长度为nnn的数组AAA和一个长度为mmm的数组BBB,Ci,j=Ai×BjC_{i,j}=A_i\times B_{j}Ci,j=Ai×Bj。有qqq轮游戏,每轮给定一个范围,小L先选一个iii,小QQQ后选一个jjj。小LLL想使Ci,jC_{i,j}Ci,j尽可能大,小Q想使Ci,jC_{i,j}Ci,j尽可能小。两人都会使用最优策略,求每轮游戏中最后的Ci,jC_{i,j}Ci,j的值
分类讨论:
1.若在范围中选择的AiA_iAi大于000,则BiB_iBi为范围中的最小值
- 若BiB_iBi最小值大于000,则AiA_iAi取大于000的最大值
- 若BiB_iBi最小值小于000,则AiA_iAi取大于000的最小值
- 若BiB_iBi最小值等于000,则AiA_iAi取大于000的任意值
2.若在范围中选择的AiA_iAi小于000,则BiB_iBi为范围中的最大值
- 若BiB_iBi最大值大于000,则AiA_iAi取小于000的最大值
- 若BiB_iBi最大值小于000,则AiA_iAi取小于000的最小值
- 若BiB_iBi最大值等于000,则AiA_iAi取小于000的任意值
3.若在范围中选择的AiA_iAi等于000,则Ci,jC_{i,j}Ci,j为000
在三种情况中取最小值就是答案
我用的是倍增,用线段树也可以过,时间复杂度为O(qlogn)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 乘方
题目大意:给出aaa和bbb,如果aba^bab小于等于10910^9109,则输出aba^bab,否则输出−1-1−1
快速幂,每做一次判断一次,时间复杂度为O(logn)O(\log n)O(logn)
直接乘也行,特判111之后,因为230>1092^{30}>10^9230>109,所以最多只会乘不超过303030次。
我用的是快速幂。
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 解密
题目大意:kkk次询问,每次给出n,e,dn,e,dn,e,d,求正整数p,qp,qp,q,使n=p×q,e×d=(p−1)(q−1)+1n=p\times q,e\times d=(p-1)(q-1)+1n=p×q,e×d=(p−1)(q−1)+1
p×q=np\times q=np×q=n
p+q=p×q−(p×q−p−q+2)+2=n−e×d+2p+q=p\times q-(p\times q-p-q+2)+2=n-e\times d+2p+q=p×q−(p×q−p−q+2)+2=n−e×d+2
p×q,p+qp\times q,p+qp×q,p+q已知,那么p,qp,qp,q就是方程x2−(p+q)x+p×q=0x^2-(p+q)x+p\times q=0x2−(p+q)x+p×q=0的两个根,用求根公式即可求出,最后判断是否为正整数即可。
求根公式:x1=−b+b2−4ac2a,x2=−b−b2−4ac2ax_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。称以上两种情况为短路。求逻辑表达式的值,以及&短路的次数和|短路的次数。如果某处短路包含在更外层的短路,则这处短路不计。
设gt(l,r)gt(l,r)gt(l,r)表示求逻辑表达式中[l,r][l,r][l,r]部分&和|分别短路的次数,返回该部分的值。那么在处理[l,r][l,r][l,r]的时候:
- 如果这一段有|且不被括号包含,则处理|之前的,如果=1,则|短路一次
- 否则如果这一段有&且不被括号包含,则处理&之前的,如果=0,则&短路一次
- 如果这一段有|或&但都被括号包含,那l,rl,rl,r的位置一定是左右括号,gt(l+1,r−1)gt(l+1,r-1)gt(l+1,r−1)
- 如果这一段没有|也没有&,那么l==rl==rl==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 上升点列
题目大意:给出平面上的nnn个点和一个kkk,kkk表示可以在平面上添加kkk个点。添加后要从这n+kn+kn+k个点中选出若干个整数点并组成一个序列,使序列中任意相邻两点间的欧几里得距离恰好为111且横坐标、纵坐标值均单调不减。
首先,将点按xxx值从小到大排序,xxx值相等则按yyy值从小到大排序。
然后,我们设f[i][j]f[i][j]f[i][j]表示以第iii个点为终点,已经放了jjj个点的最长序列的长度。每两个点连一条边,共连n2n^2n2条边。依次遍历每个点,更新f[i][j]f[i][j]f[i][j]。每个点更新的时间复杂度为O(nk)O(nk)O(nk),总时间复杂度为O(n2k)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分。