暑假队测 Day1 题解报告
A-交换小球
Problem
给你 \(N\) 个球,从左到右,球的编号从 \(1\) 到 \(N\)。
现在进行 \(Q\) 次操作。
每个操作给出一个数字 \(a\),代表找到编号为 \(a\) 的球,如果这个球没在最后一个位置,则将其与它右边的球进行交换位置;否则与其左边的球交换位置。
请输出 \(Q\) 次操作后,球的摆放情况。
\(N,Q\le2\times 10^5\)
Input
第一行给出 \(N,Q\)。
接下来 \(Q\) 行,每行给出一个数字 \(a\)。
Output
如题。
Solution
考虑到 \(N\) 较小,可以开一个数组记每个编号的位置,然后更新时把该数组一并更新。
Code
#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e6+7;
int a[N],b[N];
int main()
{
int n,T;
read(n);read(T);
for(int i=1;i<=n;i++)a[i]=i,b[a[i]]=i;
while(T--)
{
int x;
read(x);
int now=b[x];
if(now!=n)b[a[now]]=now+1,b[a[now+1]]=now,swap(a[now],a[now+1]);
else b[a[now]]=now-1,b[a[now-1]]=now,swap(a[now],a[now-1]);
}
for(int i=1;i<=n;i++)printf("%d ",a[i]);
return 0;
}
B-披萨店
Problem
Solution
可以类似状压 DP,枚举所有状态,看它合不合法即可。
虽然理论上复杂度是 \(O(2^nm)\),但是实际远远达不到,可以过。
Code
#include<bits/stdc++.h>
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1<<20;
int a[N],b[N];
int main()
{
int n,m,ans=0;
read(n);read(m);
for(int i=1,x,y;i<=m;i++)
read(a[i]),read(b[i]);
for(int i=0;i<(1<<n);i++)
{
int op=0;
for(int j=1;j<=m;j++)
if(i&(1<<(a[j]-1))&&i&(1<<(b[j]-1))){op=1;break;}
if(op==0)ans++;
}
cout<<ans;
return 0;
}
C-火车调度员
Problem
给你 \(N\) 个火车,最开始它们都是独立的。
您将收到 \(T\) 个查询。按照给定的顺序处理它们。有三种类型的查询,如下所示:
- 1 x y:将火车 \(y\) 的前部连接到火车 \(x\) 的后部。保证 \(x\not=y\) 且 \(x\) 是车尾,\(y\) 是车头。
- 2 x y:断开火车前部与火车后部的连接。保证 \(x\not=y\) 且 \(x,y\) 连通。
- 3 x:从前到后打印,包含火车 \(x\) 的那一队火车的编号。
数据范围:
\(N,T\le 1\times 10^5\)
\(x,y\le N\)
Solution
链表板子,用 \(a\) 数组记录它的后面,\(b\) 数组记录它的前面。查询时先跳到最前,然后跳到最后,具体可参见代码。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e6;
int a[N],b[N];
main()
{
int n,T;
read(n);read(T);
while(T--)
{
int op,x,y;read(op);
if(op==1)read(x),read(y),a[x]=y,b[y]=x;
else if(op==2)read(x),read(y),a[x]=b[y]=0;
else
{
read(x);
y=x;int s=1;
while(a[y]){y=a[y];s++;}
while(b[x]){x=b[x];s++;}
printf("%lld",s);
while(x)printf(" %lld",x),x=a[x];
cout<<endl;
}
}
return 0;
}
D-区间最小和
Problem
Solution
使用单调栈,考虑每个值的贡献。当前的贡献可以根据前一个的贡献来求,在进行弹栈顶操作时,原来的最大值是栈顶,那就减掉栈顶 \(\times\) 个数。一直到弹不了为止,再加上本次贡献。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e6+11;
int ans,n,a[N],stk[N],top=1;
main()
{
read(n);
for(int i=1;i<=n;i++)read(a[i]);
int now=0,sum=0;
for(int i=1;i<=n;i++)
{
while(top>1&&a[stk[top]]>a[i])
{
now-=(stk[top]-stk[top-1])*a[stk[top]];
top--;
}
now+=(i-stk[top])*a[i];
sum+=now;
stk[++top]=i;
}
cout<<sum;
return 0;
}
E-PQ数
Problem
Solution
显然 \(p,q\le 1\times 10^6\),所以我们用线性筛把 \([1,1\times 10^6]\) 的质数全筛出来,然后枚举 \(q\),二分看有多少个 \(p\) 即可。
注意:在二分时有一个条件 \(prim[mid]*k>n\),注意到 \(prim[mid]*k\) 可能爆 \(\operatorname{long long}\),要把乘改成除。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e7+7;
int prim[N],cnt;
bool bb[N];
main()
{
int n;
read(n);
for(int i=2;i<=1e7;i++)
{
if(!bb[i])prim[++cnt]=i;
for(int j=1;j<=cnt;j++)
{
if(i*prim[j]>1e7)break;
bb[i*prim[j]]=1;
if(i%prim[j]==0)break;
}
}
int ans=0;
for(int i=1;i<=cnt;i++)
{
int t=prim[i],k=t*t*t;
if(k>n)break;
int l=1,r=1e4;
while(l<=r)
{
int mid=(l+r)/2;
if(prim[mid]>n/k||prim[mid]>=t||prim[mid]*k<0)r=mid-1;
else l=mid+1;
}
ans+=l-1;
}
cout<<ans;
return 0;
}
F-比赛对手
Problem
Solution
并查集,本题适用对手的对手是队友原则,开一个数组记它的第一个敌人,如果后面再次找到,那么就按上述原则合并,直到出现本队打本队。
Code
#include<bits/stdc++.h>
#define int long long
using namespace std;
void read(int &x)
{
char ch=getchar();
int r=0,w=1;
while(!isdigit(ch))w=ch=='-'?-1:1,ch=getchar();
while(isdigit(ch))r=(r<<3)+(r<<1)+(ch^48),ch=getchar();
x=r*w;
}
const int N=1e6+7;
int fa[N],e[N];
int find(int x)
{
if(x!=fa[x])fa[x]=find(fa[x]);
return fa[x];
}
void add(int x,int y)
{fa[find(x)]=find(y);}
main()
{
int n,m,ans,ok=0;
read(n);read(m);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1,x,y;i<=m;i++)
{
read(x);read(y);
if(ok==1)continue;
int a=find(x),b=find(y);
if(a==b){ans=i,ok=1;continue;}
if(e[x])add(y,e[x]);
else e[x]=y;
if(e[y])add(x,e[y]);
else e[y]=x;
}
cout<<ans;
return 0;
}
G-走道铺砖2
Problem
没有 Solution,我不会 qwq,先咕了,等我变强了再补。
话说这比赛跨度怎么这么大啊,绿直接变紫?
可以参考这篇题解。
差最后一道题,差点 AK。