Loading

CSP-J 2021 题解

T1 分糖果

Problem

给出 \(L,R,n\),求出最大的 \(x\% n\ (L\le x\le R)\)

Solution

简单的分类讨论。

\(R-L\ge n\),说明模 \(n\) 的余数 \(0\sim n-1\) 都是存在的, 那么答案就是 \(n-1\)

\(R-L<n\),这种情况下又有两种情况:

  1. \(\lfloor \frac{L}{n}\rfloor<\lfloor\frac{R}{n}\rfloor\)。说明从 \(L\)\(R\) 中经过了 \(x\to n-1\to 0\to y\) 的情况。所以答案也是 \(n-1\)
  2. \(\lfloor \frac{L}{n}\rfloor=\lfloor\frac{R}{n}\rfloor\)。此时不存在 \(n-1\),因此答案就是 \(R\% n\)

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll n,l,r,c1,c2,d1,d2;
int main()
{
	freopen("candy.in","r",stdin);
	freopen("candy.out","w",stdout);
	scanf("%lld%lld%lld",&n,&l,&r);
	if (r-l>=n) printf("%lld\n",n-1);
	else
	{
		c1=l/n;c2=l%n;
		d1=r/n;d2=r%n;
		if (c1==d1) printf("%lld\n",d2);
		else printf("%lld\n",n-1);
	} 
	fclose(stdin);fclose(stdout);
	return 0;
} 

T2 插入排序

Problem

给出一个含有 \(n\) 个元素的数组 \(a\)。有 \(m\) 次操作,每次可以修改某个数的值或者查询某个数的排名。

修改操作最多 5000 次。

Solution

这题原本跟平衡树有点关系,但是看到限制:

至多有 5000 次操作属于类型一。

最多进行 5000 次修改。这意味着一次修改后求出所有元素的排名,到下次修改前的查询都是可以 \(O(1)\) 查询的。

那么我们只需要 \(O(n)\) 修改就可以轻松通过此题。

可以先预处理出每个数的排名,每次修改判断改后的值和改前的值的大小关系,选择向后还是向前修改。

Code

#include<bits/stdc++.h>
#define N 8005
using namespace std;
struct node
{
	int id,val;
}a[N];
int n,q,x,y,opt,rk[N];
bool lst;
bool cmp(node x,node y) 
{
	if (x.val<y.val) return true;
	if (x.val>y.val) return false;
	return x.id<y.id;
}
int main()
{
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	scanf("%d%d",&n,&q);
	for (int i=1;i<=n;++i)
		scanf("%d",&a[i].val),a[i].id=i;
	sort(a+1,a+n+1,cmp);
	for (int i=1;i<=n;++i)
		rk[a[i].id]=i;
	while (q--)
	{
		scanf("%d",&opt);
		if (opt==1)
		{
			scanf("%d%d",&x,&y);
			x=rk[x];
			if (y<a[x].val)
			{
				a[x].val=y;
				for (int i=x-1;i;--i)
				{
					if (a[i].val>a[i+1].val||(a[i].val==a[i+1].val&&a[i].id>a[i+1].id))
					{
						node t;
						t=a[i];a[i]=a[i+1];a[i+1]=t;
					}
				}
			}
			else if (y>a[x].val)
			{
				a[x].val=y;
				for (int i=x;i<n;++i)
				{
					if (a[i].val>a[i+1].val||(a[i].val==a[i+1].val&&a[i].id>a[i+1].id))
					{
						node t;
						t=a[i];a[i]=a[i+1];a[i+1]=t;
					}
				}
			}
			for (int i=1;i<=n;++i)
				rk[a[i].id]=i;
		}
		if (opt==2)
		{
			scanf("%d",&x);
			printf("%d\n",rk[x]);
		}
	}
	fclose(stdin);fclose(stdout); 
	return 0;
}

T3 网络连接

Problem

\(n\) 台计算机,每台要么为服务机要么为用户机。

每台计算机有一个地址 \(s\),形如 \(a.b.c.d:e\),满足 \(0\le a,b,c,d\le 255,0\le e\le 65535\),且 \(a,b,c,d,e\) 都没有前导 0。

对于每台服务机,若地址不合法输出 \(\text{ERR}\),若地址已经出现过输出 \(\text{FAIL}\),否则输出 \(\text{OK}\)

对于每台客户机,若地址不合法输出 \(\text{ERR}\),若地址出现过则输出对应服务机,否则输出\(\text{FAIL}\)

Solution

比较明显的一个哈希。

首先先判断地址的合法性,注意前导0,\(a,b,c,d,e\) 的大小范围和 \(.\) 以及 \(:\) 的个数。

\(a,b,c,d,e\) 拼起来成为一个长度最大为 17 的数字,可以用 \(\text{long long}\) 存储。

判断该数字是否出现过,并根据计算机的类型输出对应的答案。

Code

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,sum,num1,num2,x,y,opt;
ll res;
bool bj;
char ch,lst,llst;
struct node
{
	int id;
	ll val;
}h[19260820];
void charu(ll val,int id)
{
	ll s=val%19260817;
	while (h[s].val)
	{
		if (h[s].val==s)
		{
			x=2;
			return;
		}
		++s;
		if (s==19260817) s=0;
	}
	h[s].val=s;
	h[s].id=id;
	x=1;
}
void chazhao(ll val)
{
	ll s=val%19260817;
	while (h[s].val)
	{
		if (h[s].val==s)
		{
			y=h[s].id;
			x=1;
			return;
		}
	}
	x=2;
}
int main()
{
	freopen("network.in","r",stdin);
	freopen("network.out","w",stdout);
	scanf("%d",&n);
	ch=getchar();
	for (int i=1;i<=n;++i)
	{
		while (ch!='S'&&ch!='C') ch=getchar();
		if (ch=='S') opt=1;
		else opt=2;
		while ((ch<'0'||ch>'9')&&ch!='.'&&ch!=':') ch=getchar();
		bj=true;num1=num2=0;sum=0;res=0;
		lst=' ';llst=' ';
		while ((ch>='0'&&ch<='9')||ch=='.'||ch==':')
		{
			if (ch>='0'&&ch<='9'&&lst=='0'&&(llst=='.'||llst==':'||llst==' ')) bj=false;
			if ((ch=='.'||ch==':'||ch==' ')&&(lst=='.'||lst==':'||lst==' ')) bj=false;
			if (ch>='0'&&ch<='9') 
			{
				if (sum<=65535) sum=sum*10+(ch-'0'),res=res*10+(ch-'0');
			}
			else
			{
				if (sum>255) bj=false;
				sum=0;
				if (ch=='.') ++num1;
				if (ch==':') 
				{
					++num2;
					if (num1!=3) bj=false;
				}
			}
			llst=lst;lst=ch;
			ch=getchar();
		}
		if (sum>65535||!bj||num1!=3||num2!=1||lst==':') printf("ERR\n");
		else
		{
			x=y=0;
			if (opt==1)
			{
				charu(res,i);
				if (x==1) printf("OK\n");
				else printf("FAIL\n");
			}
			else
			{
				chazhao(res);
				if (x==1) printf("%d\n",y);
				else printf("FAIL\n");
			}
		}
	}
	fclose(stdin);fclose(stdout);
	return 0;
}

T4 小熊的果篮

Problem

给出一个长度为 \(n\) 的 01 串,每次删除连续的 1 或 0 的第一个 1/0 ,问每次删去的数字的编号。

注意删除数字后存在原本不连续的变得连续。

Solution

考虑将每个块合并,例如样例 2 就可以合并成 4 3 3 2 1 1 2 4。

把各个块存入队列中,每次取出各个块的首位,更新,合并。

考虑时间复杂度,有一种卡到最劣的构造是 1 2 3 4…… \(\sqrt{n}\)

易证这种情况下的时间复杂度为 \(\mathcal{O}(n\sqrt{n})\)

Code

#include<queue>
#include<cstdio>
#define N 200005
using namespace std;
struct node
{
	int st,ed,cl;
};
queue<node> q,qq;
int n,num,c[N];
bool b[N];
int main()
{
	freopen("fruit.in","r",stdin);
	freopen("fruit.out","w",stdout);
	scanf("%d",&n);
	for (int i=1;i<=n;++i)
		scanf("%d",&c[i]);
	c[n+1]=1-c[n];
	for (int i=2,lst=1;i<=n+1;++i)
		if (c[i]!=c[i-1]) q.push((node){lst,i-1,c[i-1]}),lst=i;
	num=n;
	while (num)
	{
		while (!q.empty())
		{
			node x=q.front();
			q.pop();
			while (b[x.st]&&x.st<=x.ed) x.st++;
			if (x.st>x.ed) continue;
			printf("%d ",x.st);
			--num;b[x.st]=true;
			if (x.st==x.ed) continue;
			x.st++;
			qq.push(x);
		}
		printf("\n");
		while (!qq.empty())
		{
			node x=qq.front();
			qq.pop();
			while (!qq.empty())
			{
				node y=qq.front();
				if (x.cl==y.cl) 
				{
					x.ed=y.ed;
					qq.pop();
				}
				else break;
			}
			q.push(x);
		}
	}
	return 0;
}
posted @ 2021-11-11 16:45  Thunder_S  阅读(285)  评论(0编辑  收藏  举报