Test 2022.09.29

今天是\(SHOI\)专场

T1 智力大冲浪

分析

确实是智力题了,考虑贪心策略:优先安排价值大的,时间能往后就往后,这样前面能选更多东西,如果当前的时间已经有安排了,那么就往前一个位置挪,直到当前时间没有被安排,如果为\(0\),那么久不选当前的东西了。

Code

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define int long long
using namespace std;
const int maxn=600;
struct present{int date,fine;}a[maxn];
bool cmp(present a,present b){return a.fine!=b.fine?a.fine>b.fine:a.date>b.date;}
int n,m;
int vis[maxn];
int all=0;
signed main()
{
	scanf("%lld%lld",&m,&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i].date);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i].fine),all+=a[i].fine;
	sort(a+1,a+n+1,cmp);
	int sum=0;
	for(int i=1;i<=n;++i)
	{
		int p=a[i].date;
		while(vis[p]==1)p--;
		if(p==0){continue;}
		vis[p]=1,sum+=a[i].fine;
	}
	printf("%lld",m-(all-sum));
	return 0;
} 
/*
10000 7
4 2 4 3 1 4 6 
70 60 50 40 30 20 10

10
7
1 2 2 4 2 5 5
0 1 1 1 2 100 1
*/

T2 发微博

分析

本来想着暴力建图,对于每一个加号都暴力遍历然后让\(ans_i++\),然而人傻不会\(vector\)的删除(好像这样可以骗到\(80pts\)),最后只做了一个\(50pts\)的暴力。

突破口

就是题面中的"保证操作合法:即 + x y 时 x 和 y 一定不是好友,而 − x y 时 x 和 y 一定是好友"

再次分析

上一句话是什么意思呢?这是在变相告诉我们:给出的操作一定是若干对"+ -"!!。
可能这个时候还是没反应过来,很正常,看下去就懂了。
很显而易见的是,只有被囊括在这一对对"\(+\quad -\)"里面的"!"操作才会对答案进行贡献,如果暴力遍历每一对所有\(+,-\)里的内容是完全会超时的。
这个时候我们就可以考虑借助后缀和的思想统计,众所周知,"!"操作只有在"+"进行后才能对答案产生贡献,在“-”操作进行后停止对答案产生贡献。
那我们不妨假设"+"之后的"!"操作会一直对答案产生贡献,不受"-"的影响,这样就会多算一部分的贡献值,考虑如何去消除它?很简单,只要减去"-"之后"!"的数量就行了。
这样的话我们就不需要暴力遍历了,只用,倒序遍历,分别统计每个人对应"!"的后缀和\(sum\).
对于每一个"+ x y"的操作,我们把\(ans_x+sum_y,ans_y+sum_x\)
对于每一个"- x y"的操作,我们把\(ans_x-sum_y,ans_y-sum_x\)
接下来应该不用讲了 上代码

Code

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+100;
int ans[maxn],cnt[maxn];
char a[maxn];int x[maxn],y[maxn];
int main()
{
	ios::sync_with_stdio(0);
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>a[i]>>x[i];
		if(a[i]=='+'||a[i]=='-')cin>>y[i];	
	}	
	for(int i=m;i>=1;i--)
	{
		if(a[i]=='!')cnt[x[i]]++;
		else if(a[i]=='-')ans[x[i]]-=cnt[y[i]],ans[y[i]]-=cnt[x[i]];
		else if(a[i]=='+')ans[x[i]]+=cnt[y[i]],ans[y[i]]+=cnt[x[i]]; 
	}
	for(int i=1;i<n;i++)cout<<ans[i]<<' ';
	cout<<ans[n];
	return 0;
}

T3 发牌

心路历程

考场上面再次成功想歪了,今天真是一个正解没打出来,以为是在链表上面维护倍增,结果维护就直接\(g\)掉,只有\(20pts\)

分析&正解

对于一次销牌的操作\(R_i\),其实就是把当前序列中前\(R_i\mod len\)个元素按顺序放到序列最后的位置,然后输出当前排在第一位的元素,记上一次跳到的位置是\(pos\)的话,实际上就是在查询这整个序列中第\(((pos+R_i)\mod len)+1\)小的数是谁,输出以后把他剔除掉(或许你可以把这个操作理解成循环队列?)有点难以解释,怎么说呢?从上一个查询的位置开始再跳\(R_i\)个,相当于就是查询现有区间内第\(((pos+R_i)\mod len)+1\)小,然后把\(pos\)迭代成\((pos+R_i)\mod len,len--\),那么就是权值线段树的板子题了。

点击查看代码
#include<bits/stdc++.h>
#define  re register
using namespace std;
const int maxn=5e6;
int val[maxn];
inline void read(int &x)
{
	x=0;re int f=1;char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-')f=-1;
		c=getchar();
	}
	while('0'<=c&&c<='9')
	{
		x=(x<<1)+(x<<3)+(c^48);
		c=getchar(); 
	}
	x*=f;
}
inline void wr(re int x)
{
	if(x<0)putchar('-'),x=~x+1;
	if(x>9)wr(x/10);
	putchar(x%10^'0');
}
void build(re int x,re int l,re int r)
{
	if(l==r){val[x]=1;return ;}
	re int mid=(l+r)>>1;
	build(x<<1,l,mid);build(x<<1|1,mid+1,r);
	val[x]=val[x<<1]+val[x<<1|1];
}
int query(re int x,re int l,re int r,re int v)/*查询权值为v的点在哪个位置*/
{
	val[x]--;
	if(l==r)return l;
	re int mid=(l+r)>>1;
	if(v<=val[x<<1])return query(x<<1,l,mid,v);
	else return query(x<<1|1,mid+1,r,v-val[x<<1]); 
}
int main()
{
	register int n;
	read(n);
	build(1,1,n);
	register int r,pos=0;
	for(register int len=n;len>=1;len--)
	{
		read(r);
		pos=pos+r>=len?(pos+r)%len:pos+r;
		wr(query(1,1,n,pos+1)),printf("\n");
	}
	return 0;
}

T4 阶乘字符串

\(To\quad be\quad continued\)

posted @ 2022-09-29 20:14  Hanggoash  阅读(5)  评论(0编辑  收藏  举报
动态线条
动态线条end