暑假队测 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

Atcoder原题

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

Atcoder原题

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

Atcoder原题

没有 Solution,我不会 qwq,先咕了,等我变强了再补。
话说这比赛跨度怎么这么大啊,绿直接变紫?
可以参考这篇题解

差最后一道题,差点 AK。

posted @ 2022-07-24 16:33  Epoch_L  阅读(17)  评论(0编辑  收藏  举报