8.8~8.13考试总结

8.9

wave

【题目描述】 海浪分为前浪和后浪,分别用数字 0 和数字 1 表示。 现在给定一天中 n 个时段的海浪序列,第 i 个序列包含 $m_i$ 个海浪。 全天海浪序列是 n 个时段的海浪序列按照任意的一种次序顺次拼接而成的序列。 一个涨潮定义为海浪序列的一个子序列(不必连续),满足这个子序列中的任何一个前浪 都出现在任何一个后浪之前。涨潮的强度定义为其中包含的海浪个数。 请你求出所有可能的全天海浪序列中,强度最大的涨潮的强度最大值。
【输入格式】 从文件 wave.in 中读入数据。 第一行一个正整数 n,表示序列个数。 接下来 n 行,第 i + 1 行一个正整数 $m_i$,其后紧随 $m_i$ 个数字 0 或 1,描述了一个海浪 序列。
【输出格式】 输出到文件 wave.out 中。 第一行一个正整数,表示强度最大的涨潮的强度。

\(solution\)

考虑每一个海浪序列全是1或者全是0所提供的最大贡献,与这个序列在中间的最大涨潮度相比较,保证所有序列的贡献加起来最大

AC Code
#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

const int N=5e5+5;
int n,sum[N],s[N],sum1[N],ans=0;
int m0[N],m1[N],sum2[N];

int main()
{
//	freopen("wave.in","r",stdin);
//	freopen("wave.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++)
	{
		sum[i]=read();sum[i]+=sum[i-1];
		for(int j=sum[i-1]+1;j<=sum[i];j++)
		{
			s[j]=read();
			if(s[j]==0)m0[j]=m0[j-1]+1,m1[j]=m1[j-1];
			else m1[j]=m1[j-1]+1,m0[j]=m0[j-1];
		}
		sum1[i]=max(m0[sum[i]]-m0[sum[i-1]],m1[sum[i]]-m1[sum[i-1]]);
		sum2[i]=sum2[i-1]+sum1[i];
		int tmp=0;
		ans=max(ans,m0[sum[i]]-m0[sum[i-1]]-sum1[i]);
		for(int j=sum[i];j>sum[i-1];j--)
		{
			if(s[j]==1)tmp++;
			ans=max(ans,tmp+m0[j-1]-m0[sum[i-1]]-sum1[i]);
		}
	}
	printf("%d\n",ans+sum2[n]);
	return 0;
}

scientist


\(solution\)
50pts:
枚举通知的据点 T,那么第 i 个据点的人会在 \(f_{T (i)} = a_i + |T − i|\) 分钟结束时到达道路上。所以当j > fT (j)时,送货的人会被挡住。

又因为送货的人可能在被挡住之前,他的左右两侧都有人,所以目标达成的时间为:

\[max(min_{1\leq i < j}{f_{T(i)}},min_{j\leq i \leq n}{f_{T(i)}}) \]

rain

8.10

Cytoid

\(solution\)

直接暴力,可惜考场上太谨慎,没把暴力打好

开两个前缀和矩阵,分别记录x和?,然后枚举以点(i,j)为左上角的矩形,看是否可行

AC Code
#include<bits/stdc++.h>
using namespace std;

const int N = 105;
const int mod = 998244353;
int n, m, f[N][N];
int a[N][N],b[N][N];
char s;

int qow(long long x,int y)
{
	long long res=1;
	while(y>0)
	{
		if(y&1)res*=x,res%=mod;
		x*=x;x%=mod;y>>=1;
	}
	return res%mod;
}

int check(int x, int y)
{
	int res=0;
	for(int i=x;i<=n;i++)
	{
		for(int j=y;j<=m;j++)
		{
			int tmp1=a[i][j]-a[x-1][j]-a[i][y-1]+a[x-1][y-1];
			if(tmp1!=0)continue;
			int tmp2=b[i][j]-b[x-1][j]-b[i][y-1]+b[x-1][y-1];
			if(tmp2==0)res+=1;
			else res+=1*qow(2,mod-tmp2-1);
			res%=mod;
		}
	}
	return res;
}

int main()
{
//	freopen("cai.in","r",stdin);
//	freopen("cai.out","w",stdout);
	scanf("%d%d", &n, &m);
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>s;
			if(s=='x')a[i][j]=1;
			if(s=='?')b[i][j]=1;
			a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int tmp=check(i,j);
			ans+=tmp;
			ans%=mod;
		}
	}
	printf("%d\n",ans);
	return 0;
}

\(solution\)

AC自动机+链并(计算贡献)

\(solution\)
用分块/线段树来处理区间查询和区间修改就好了

AC Code
#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(!isdigit(ch))
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(isdigit(ch))
	{
		x=x*10+int(ch-'0');
		ch=getchar();
	}
	return x*f;
}

const int N=25e4+10;

int n,m;
string str;

struct ww{
	int l,r,len,tag;
	int sum,num[11];
}tr[N<<3];

void pushup(int p)
{
	tr[p].sum=tr[p<<1].sum+tr[p<<1|1].sum;
	for(int i=0;i<10;++i)
		tr[p].num[i]=tr[p<<1].num[i]+tr[p<<1|1].num[i];
	return ;
}

void build(int p,int l,int r)
{
	tr[p].l=l,tr[p].r=r;
	tr[p].len=r-l+1;
	tr[p].tag=0;
	if(l==r)
	{
		tr[p].sum=int(str[l-1]-'0');
		++tr[p].num[tr[p].sum];
		return ;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
	return ;
}


void add(int p,int x)
{
	if(!x) return ;
	x%=10;
	int lsum=0;
	for(int i=10-x;i<10;++i)
		lsum+=tr[p].num[i];
	tr[p].sum+=x*tr[p].len-lsum*10;
	int a[10];
	for(int i=0;i<10;++i)
		a[i]=tr[p].num[i];
	for(int i=0;i<10;++i)
		tr[p].num[(i+x)%10]=a[i];
	return ;
}

void pushdown(int p)
{
	tr[p].tag%=10;
	if(tr[p].tag)
	{
		add(p<<1,tr[p].tag);
		add(p<<1|1,tr[p].tag);
		tr[p<<1].tag+=tr[p].tag;
		tr[p<<1|1].tag+=tr[p].tag;
		tr[p].tag=0;
	}
	return ;
}

void change(int p,int l,int r)
{
	int l1=tr[p].l,r1=tr[p].r;
	if(l<=l1 && r1<=r)
	{
		add(p,1);
		(tr[p].tag+=1)%=10;
		return ;
	}
	pushdown(p);
	int mid=(l1+r1)>>1;
	if(l<=mid) change(p<<1,l,r);
	if(mid<r) change(p<<1|1,l,r);
	pushup(p);
	return ;
}

int ask(int p,int l,int r)
{
	int l1=tr[p].l,r1=tr[p].r;
	if(l<=l1 && r1<=r)
	{
		return tr[p].sum;
	}
	pushdown(p);
	int cnt=0;
	int mid=(l1+r1)>>1;
	if(l<=mid) cnt+=ask(p<<1,l,r);
	if(mid<r) cnt+=ask(p<<1|1,l,r);
	pushup(p);
	return cnt;
}
int main()
{
//	freopen("1.in","r",stdin);
//	freopen("2.out","w",stdout);
	n=read(),m=read();
	cin>>str;
	int l,r;
	build(1,1,n);
	while(m--)
	{
		l=read(),r=read();
		printf("%d\n",ask(1,l,r));
		change(1,l,r);
	}
	return 0;
}

8.13

冒泡排序(sort)

\(solution\)
30pts:可知需要求的轮数等于\(max_{i=1}^{n}{i-r_i}\),即原位置与大小排序的位置的最大差值

100pts:用值域线段树在区间[l,r]中记录\(max_{i=l}^{r}{p_i-\sum_{j=l}^{i}{s_j}}\)\(\sum_{i=l}^{r}{s_i}\),其中\(p_i\)表示i在序列中最后一次出现的位置,\(s_i\)表示i在序列中出现的次数

AC Code
#include<bits/stdc++.h>
using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

const int N=5e5+5;
int a[N];
set<int>s[N];
int tr[N*4],sum[N*4];

void updat(int p)
{
	sum[p]=sum[p<<1]+sum[p<<1|1];
	tr[p]=max(tr[p<<1],tr[p<<1|1]-sum[p<<1]);
}

void add(int p,int l,int r,int x)
{
	if(l==r&&l==x)
	{
		sum[p]=s[x].size();
		if(sum[p])tr[p]=*s[x].rbegin()-sum[p];
		else tr[p]=0; 
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)add(p<<1,l,mid,x);
	else add(p<<1|1,mid+1,r,x);
	updat(p);
}

int main()
{
	int n=read(),q=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		s[a[i]].insert(i);
		add(1,1,N,a[i]);
	}
	int x,y;
	for(int i=1;i<=q;i++)
	{
		x=read(),y=read();
		s[a[x]].erase(x);
		add(1,1,N,a[x]);
		a[x]=y;
		s[a[x]].insert(x);
		add(1,1,N,a[x]);
		cout<<tr[1]<<endl;
	}
	return 0;
}

图上描点

最优选择

posted @ 2022-08-15 09:57  两只风小鱼  阅读(47)  评论(0)    收藏  举报