NOIP模拟55

T1 Skip

解题思路

正解给的是线段树维护单调栈,但是我不会。。 CDQ 维护斜率可做!!!

先得出一个朴素的 DP 方程:设 \(f_i\) 表示最后一场是 i 的最优解。

转移方程就是 \(f_i=\max\limits_{1\le j<i}\{f_j+s_i-\binom{i-j}{2}\}\)

然后考虑优化,转移 i 时,当 \(j>k\) 且 j 优于 k 当且仅当:

\[f_j-\dfrac{j^2+j}{2}+ij>f_k-\dfrac{k^2+k}{2}+ik \]

\(g(x)=f_x-\dfrac{x^2+x}{2}\) 上述条件就变成了:

\[\dfrac{g(j)-g(k)}{j-k}>-i \]

那么我们就可以对于横坐标为数组下标,纵坐标为 \(g(x)\) 维护一个上凸包。

然后就是 CDQ 优化斜率了,注意一个点,先处理左边的子区间,然后处理当前区间,最后处理右边的子区间就好了。

由于要在队列里面保留一个元素,因此更新答案时需要判断队首元素是否合法。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e5+10,INF=1e18;
int n,ans=-INF,f[N],q[N];
struct Node{int id,dat;}s[N];
bool comp1(Node x,Node y){return (x.dat==y.dat)?x.id<y.id:x.dat<y.dat;}
bool comp2(Node x,Node y){return x.id<y.id;}
int g(int x){return f[x]-(x*x+x)/2;}
void CDQ(int l,int r)
{
	if(l==r) return f[s[l].id]=max(f[s[l].id],s[l].dat-(s[l].id-1)*s[l].id/2),void() ;
	int mid=(l+r)>>1,i=l,j=mid+1,head=1,tail=0; CDQ(l,mid);
	sort(s+l,s+mid+1,comp2); sort(s+mid+1,s+r+1,comp2);
	while(j<=r)
		if(i<=mid&&s[i].id<s[j].id)
		{
			while(head<tail&&(g(s[i].id)-g(s[q[tail]].id))*(s[q[tail]].id-s[q[tail-1]].id)>=(g(s[q[tail]].id)-g(s[q[tail-1]].id))*(s[i].id-s[q[tail]].id)) tail--;
			q[++tail]=i; i++;
		}
		else
		{
			while(head<tail&&(g(s[q[head+1]].id)-g(s[q[head]].id))>=-s[j].id*(s[q[head+1]].id-s[q[head]].id)) head++;
			if(s[q[head]].id<s[j].id) f[s[j].id]=max(f[s[j].id],g(s[q[head]].id)+s[j].dat-(s[j].id*s[j].id-2*s[j].id*s[q[head]].id-s[j].id)/2); j++;
		}
	sort(s+mid+1,s+r+1,comp1); CDQ(mid+1,r);
}
signed main()
{
	freopen("skip.in","r",stdin); freopen("skip.out","w",stdout);
	n=read(); memset(f,128,sizeof(f));
	for(int i=1;i<=n;i++) s[i].id=i,s[i].dat=read();
	sort(s+1,s+n+1,comp1);CDQ(1,n);
	for(int i=1;i<=n;i++) ans=max(ans,f[i]-(n-i+1)*(n-i)/2);
	printf("%lld",ans); return 0;
}

T2 String

解题思路

首先,有一个结论:对于 \(k>8\) 的情况,其实只要在前面输出一些 abababa...cdcdcd...efefef 就好了。

对于 \(k\le 8\) 的情况其实就是一个记忆化搜索,因为出现次数最多是 8 ,因此我们可以压位,状态就是出现次数。

然后搜索的时候对于搜过的部分小于 rk 的直接搜下去,大于等于 rk 直接返回可以减掉的排名就好了。

搜索的时候同时记录一下每种字母出现的次数以及每种总数出现的个数。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=1e3+10;
int k,base,ranking,ending,cnt[N],tot[N],ch[N];
unordered_map<int,int> mp[10];
int sto(int x){if(!x)return x;return 1ll<<(4*(x-1));}
int dfs(int x,int sta,int rk,int res)
{
	if(mp[res].find(sta)!=mp[res].end()&&rk>=mp[res].find(sta)->second) return mp[res].find(sta)->second; 
	if(sta==ending)
	{
		if(!rk){for(int i=1;i<x;i++) printf("%c",(char)(ch[i]+'a'));exit(0);}
		return mp[res].insert(make_pair(sta,1)),1;
	}
	int all=0;
	for(int i=base,temp;i<26;i++)
		if(i!=ch[x-1])
		{
			ch[x]=i; cnt[i]++; tot[cnt[i]]++; temp=0;
			if(tot[cnt[i]]<=k-cnt[i]+1)
				temp=dfs(x+1,sta-sto(cnt[i]-1)+sto(cnt[i]),rk,cnt[i]);
			all+=temp; rk-=temp; tot[cnt[i]]--; cnt[i]--;
		}
	return mp[res].insert(make_pair(sta,all)),all;
}
signed main()
{
	freopen("string.in","r",stdin); freopen("string.out","w",stdout);
	k=read(); ranking=read(); ch[0]=-1;
	while(k>8)
	{
		for(int i=1;i<k;i++) printf("%c%c",(char)(base+'a'),(char)(base+'b'));
		printf("%c",(char)(base+'a')); k-=2; base+=2;
	}
	for(int i=1;i<=k;i++) ending|=sto(i);
	dfs(1,0,ranking-1,0); printf("-1");
	return 0;
}

T3 Permutation

解题思路

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=5e6+10,mod=1e9+7;
int n,m,k,ans,fac[N],ifac[N];
int power(int x,int y){x%=mod;int temp=1;while(y){if(y&1)temp=temp*x%mod;x=x*x%mod;y>>=1;}return temp;}
int C(int x,int y){return fac[y]*power(fac[x]*fac[y-x],mod-2)%mod;}
signed main()
{
	freopen("perm.in","r",stdin); freopen("perm.out","w",stdout);
	n=read(); k=read(); m=read(); if(m==1){printf("%lld",n-k+m-1);return 0;}
	fac[0]=ifac[0]=1; for(int i=1;i<=1000000;i++) fac[i]=fac[i-1]*i%mod;
	ifac[n]=power(fac[n],mod-2); for(int i=n-1;i>=1;i--) ifac[i]=ifac[i+1]*(i+1)%mod;
	n-=k-m;k=m; for(int i=2;i<=k;i++) ans=(ans+C(k-i+2,n-i))%mod;
	for(int i=2;i<=n;i++) ans=(ans+C(k-2,n-i-1)*(i-1)%mod)%mod;
	printf("%lld",ans); return 0;
}

T4 小P的生成树

解题思路

显然我们跑最小(或者最大)生成树需要的只是一个相对的大小关系。

考虑虚数在坐标轴上的表示是向量,因此就可以推出通过任意两个向量相加得出的方向当作边界,所得到的几个区间中相对大小关系都是相同的。

因此我们直接枚举这几个边界跑最大生成树并且记录 a 和 b 的和最后答案取个最大值就好了。

当然有个 \(\mathcal{O}{m^2logm}\) 的做法,但是我太菜了不会。。。

code

#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define f() cout<<"Failed"<<endl
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
const int N=60,M=210;
const double pi=acos(-1.0);
struct Node
{
	int l,r,a,b;
	double len;
	bool friend operator < (Node x,Node y){return x.len>y.len;}
}s[M];
int n,m,cnt,fa[N];
double ans,f[M*M*4];
int find(int x)
{
	if(x==fa[x]) return x;
	return fa[x]=find(fa[x]);
}
void solve(double jiao)
{
	double Sa=0,Sb=0;
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++)
		s[i].len=cos(jiao)*s[i].a+sin(jiao)*s[i].b;
	sort(s+1,s+m+1);
	for(int i=1;i<=m;i++)
	{
		int x=s[i].l,y=s[i].r;
		if(find(x)==find(y)) continue;
		fa[find(x)]=find(y);
		Sa+=s[i].a; Sb+=s[i].b;
	}
	ans=max(ans,sqrt(Sa*Sa+Sb*Sb));
}
signed main()
{
	freopen("mst.in","r",stdin); freopen("mst.out","w",stdout);
	n=read(); m=read();
	for(int i=1;i<=m;i++) s[i].l=read(),s[i].r=read(),s[i].a=read(),s[i].b=read();
	for(int i=1;i<=m;i++)
		for(int j=i+1;j<=m;j++)
		{
			double x=s[i].a+s[j].a,y=s[i].b+s[j].b;
			f[++cnt]=acos(y/sqrt(x*x+y*y));
			f[++cnt]=acos(y/sqrt(x*x+y*y))+pi;
			f[++cnt]=acos(y/sqrt(x*x+y*y))+pi/2.0;
		}
	f[++cnt]=-pi/2.0;
	for(int i=1;i<=cnt;i++) solve(f[i]);
	printf("%.6lf",ans);
	return 0;
}
posted @ 2021-09-19 15:00  Varuxn  阅读(200)  评论(0编辑  收藏  举报