JXOI2017题解

「JXOI2017」加法

先二分答案,求出每个位置需要被覆盖多少次,那么从左往右扫,对于每个位置,如需被覆盖 x x x次,那就贪心的选择前 x x x个能覆盖到他且覆盖的最远的 x x x条线段。随便维护一下即可。

Code

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=200010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,m,K,a[Maxn];LL b[Maxn],A;
struct Line{int l,r;}p[Maxn];
struct Seg{int l,r,lc,rc,mx,tag;}tr[Maxn<<1];
int tot;
void Add(int x,int v){tr[x].mx+=v,tr[x].tag+=v;}
void up(int x)
{
	int lc=tr[x].lc,rc=tr[x].rc;
	tr[x].mx=max(tr[lc].mx,tr[rc].mx);
}
void down(int x)
{
	int t=tr[x].tag;
	if(t)tr[x].tag=0,Add(tr[x].lc,t),Add(tr[x].rc,t);
}
void build(int l,int r)
{
	int x=++tot;
	tr[x].l=l;tr[x].r=r;tr[x].tag=0;
	if(l==r){tr[x].mx=b[l];return;}
	int mid=l+r>>1;
	tr[x].lc=tot+1,build(l,mid);
	tr[x].rc=tot+1,build(mid+1,r);
	up(x);
}
void add(int x,int l,int r,int v)
{
	if(tr[x].l==l&&tr[x].r==r){Add(x,v);return;}
	int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
	down(x);
	if(r<=mid)add(lc,l,r,v);
	else if(l>mid)add(rc,l,r,v);
	else add(lc,l,mid,v),add(rc,mid+1,r,v);
	up(x);
}
int pos,v;
void go(int x)
{
//	printf("%d\n",x); 
	if(tr[x].l==tr[x].r){pos=tr[x].l;v=tr[x].mx;return;}
	int lc=tr[x].lc,rc=tr[x].rc;
	down(x);
	if(tr[lc].mx>0)go(lc);
	else go(rc);
}
vector<int>h[Maxn];
bool check(LL o)
{
	priority_queue<int>q;
	for(int i=1;i<=n;i++)b[i]=max(0LL,(o-a[i]+A-1)/A);
	tot=0;build(1,n);
	int u=0,pp=0;
	while(tr[1].mx&&u<K)
	{
		go(1);
		while(pp<pos)
		{
			pp++;
			for(int i=0;i<h[pp].size();i++)q.push(h[pp][i]);
		}
		if(u+v>K)return false;
		u+=v;
		while(v--)
		{
			if(q.empty())return false;
			if(q.top()<pos)return false;
			add(1,pos,q.top(),-1);q.pop();
		} 
	}
	return(!tr[1].mx&&u<=K);
}
int main()
{
	int T=read();
	while(T--)
	{
		n=read(),m=read(),K=read(),A=read();
		int mn=inf; 
		for(int i=1;i<=n;i++)a[i]=read(),mn=min(mn,a[i]);
		for(int i=1;i<=m;i++)
		{
			int l=read(),r=read();
			h[l].push_back(r);
		}
		LL l=mn,r=mn+K*A;
		while(l<=r)
		{
			LL mid=l+r>>1;
			if(check(mid))l=mid+1;
			else r=mid-1;
		}
		printf("%lld\n",l-1);
		for(int i=1;i<=n;i++)
		{
			while(h[i].size())h[i].pop_back();
		}
	} 
}

「JXOI2017」数列

DP, f i , j , k , l f_{i,j,k,l} fi,j,k,l表示到第 i i i个位置,前驱为 j j j,后继为 k k k,当前值为 l l l的方案数,滚动数组优化一下空间,前缀和优化一下转移就可以了。

Code

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=55,Maxm=153;
const int inf=2147483647;
const int mod=998244353;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
void upd(int&x,int y){x+=y;if(x>=mod)x-=mod;}
int n,a[Maxn],f[2][Maxm][Maxm][Maxm],s1[2][Maxm][Maxm],s2[2][Maxm][Maxm],s3[2][Maxm];
int main()
{
	n=read();
	for(int i=1;i<=n;i++)a[i]=read();
	int o=0,ans=0;
	for(int i=1;i<=a[1];i++)f[0][0][151][i]=1;
	for(int l=0;l<=151;l++)
	for(int r=l+1;r<=151;r++)
	{
		s1[o][l][r]=0;
		for(int p=r+1;p<=151;p++)upd(s1[o][l][r],f[o][l][p][r]);
	}
	for(int l=0;l<=151;l++)
	for(int r=l+1;r<=151;r++)
	{
		s2[o][l][r]=0;
		for(int p=l-1;p>=0;p--)upd(s2[o][l][r],f[o][p][r][l]);
	}
	for(int p=0;p<=151;p++)
	{
		s3[o][p]=0;
		for(int l=0;l<=p;l++)
		for(int r=p;r<=151;r++)
		upd(s3[o][p],f[o][l][r][p]);
	}
	for(int i=2;i<=n;i++)
	{
		o^=1;
		for(int l=0;l<=151;l++)
		for(int r=l+1;r<=151;r++)
		s1[o][l][r]=s2[o][l][r]=0;
		for(int p=0;p<=151;p++)s3[o][p]=0;
		for(int l=0;l<=151;l++)
		for(int r=l;r<=151;r++)
		{
			if(l==r&&l>0&&l<=a[i])
			{
				f[o][l][l][l]=s3[o^1][l];
				upd(s3[o][l],f[o][l][l][l]); 
				if(i==n)upd(ans,f[o][l][l][l]);
			}
			for(int p=max(1,l+1);p<=min(a[i],r-1);p++)
			{
				f[o][l][r][p]=(s1[o^1][l][r]+s2[o^1][l][r])%mod;
				int t=f[o][l][r][p];
				upd(s1[o][l][p],t);
				upd(s2[o][p][r],t);
				upd(s3[o][l],t),upd(s3[o][r],t),upd(s3[o][p],t);
				if(i==n)upd(ans,t);
			}
		}
	}
	printf("%d",ans);
}

「JXOI2017」颜色

求出每个位置的前驱后继,如果没有的话就设为自己的位置,那么就是求有多少个区间 [ l , r ] [l,r] [l,r],满足 n e x t r next_r nextr为这段 n e x t next next的最大值, p r e l pre_l prel为这段 p r e pre pre的最小值,先用两个单调栈搞一搞就好了,本以为要线性,但是带了一个 l o g log log还是跑到了LOJ r a n k 2 rank2 rank2

Code

#include
using namespace std;
#define LL long long
#define pa pair
const int Maxn=300010;
const int inf=2147483647;
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<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*f;
}
int n,a[Maxn],pre[Maxn],nxt[Maxn],sta[Maxn],top,pos[Maxn],L[Maxn];LL ans;
int main()
{
	int T=read();
	while(T--)
	{
		n=read();ans=0;
		for(int i=1;i<=n;i++)pos[i]=pre[i]=nxt[i]=0;
		for(int i=1;i<=n;i++)
		{
			a[i]=read();
			if(pos[a[i]])pre[i]=pos[a[i]],nxt[pos[a[i]]]=i;
			else pre[i]=i;
			pos[a[i]]=i;
		}
		for(int i=1;i<=n;i++)if(!nxt[i])nxt[i]=i;
		top=0;
		for(int i=1;i<=n;i++)
		{
			while(top&&nxt[i]>=nxt[sta[top]])top--;
			if(!top)L[i]=1;else L[i]=sta[top]+1;
			sta[++top]=i;
		}
		top=0;
		for(int i=1;i<=n;i++)
		{
			while(top&&pre[i]<pre[sta[top]])top--;
			if(i==pre[i])sta[++top]=i;
			if(i==nxt[i])
			{
				int l=1,r=top;
				while(l<=r)
				{
					int mid=l+r>>1;
					if(sta[mid]>=L[i])r=mid-1;
					else l=mid+1;
				}
				r++;
				ans+=(top-r+1);
			}
		}
		printf("%lld\n",ans);
	}
}

你可能感兴趣的:(其他)