省选模拟赛第一场

迷你高尔夫 大致题意是这样的 n个人m次击球 每次击球都会有一个得分 对于每个人都可以规定一个A 用A来代替某次击球的大于A的得分 求每个人最佳排名。

如此nb的题目 考虑暴力枚举A值 暴力扫复杂度极高 会T 优化一下 可以sort然后再扫+二分复杂度还是很高

考虑优化掉枚举A值这个步骤,这需要发现问题的特异性,一个显然的结论是一个人可以完爆另外一个人不过这是一个多维偏序的问题 难以解决.

我们考虑逐人判断其排名 显然是可以二分的,考虑如何check 很困难,做不了。

进一步的我们可以发现A值的意义A值偏大排名可能较高 A值较小排名可能还是较高 这会不会是一个单峰函数呢?A取恰当值才会有奇效呢?可以一试。

T2 不会写 T3 sb字符串也不会 先刚T1吧。

并非单峰函数 因为是没有一定的单调性的 我自闭了。

想一个靠谱的做法再说.

T3 我们称一个字符串A能被字符串B覆盖,当且仅当A中的每一个位置都至少被包含在一个与B相同的A的子串。 举个例子:abaabaababa就可以被aba覆盖。 30%的数据:|S|<=100 50%的数据:|S|<=1000 100%的数据:1<=|S|<=200000

先挖掘性质 显然一个子串能够形成循环 说明必然其B是其前缀的一部分我们暴力枚举B对A进行KMP看能形成完全覆盖不能.

复杂度$S^2$ 得到了50分. ``` const int MAXN=200010; char a[MAXN],b[MAXN]; int n; int nex[MAXN],w[MAXN],c[MAXN],en[MAXN]; inline void pp(int x) { for(int i=1;i<=x;++i) { b[i]=a[i]; nex[i]=0; } int j=0; for(int i=2;i<=x;++i) { while(j&&b[j+1]!=b[i])j=nex[j]; if(b[j+1]==b[i])++j; nex[i]=j; } j=0; memset(c,0,sizeof(c)); memset(en,0,sizeof(en)); for(int i=1;i<=n;++i) { while(j&&a[i]!=b[j+1])j=nex[j]; if(b[j+1]==a[i])++j; if(j==x) { ++c[i-x+1]; --c[i+1]; en[i]=1; j=nex[j]; } } int flag=1; for(int i=1;i<=n;++i) { c[i]+=c[i-1]; if(c[i]&&flag&&en[i])w[i]=min(w[i],x); if(!c[i])flag=0; } } int main() { freopen("cover.in","r",stdin); freopen("cover.out","w",stdout); scanf("%s",a+1); memset(w,0x3f,sizeof(w)); n=strlen(a+1); for(int i=1;i<=n;++i)pp(i); for(int i=1;i=i-last[ans[j]])ans[i]=ans[j]; else ans[i]=i; last[ans[i]]=i; } } int main() { freopen("cover.in","r",stdin); freopen("cover.out","w",stdout); scanf("%s",a+1); n=strlen(a+1); solve(); for(int i=1;i<=n;++i)printf("%d ",ans[i]); return 0; } ``` 进一步的考虑一下为什么 ? 一个结论这个方法主要应用是前缀和后缀的匹配 一个串的后缀的最长能匹配的前缀的长度设覆盖整个串的循环串长度为A那么如果A>L L表示前缀和后缀的最大匹配长度.这种情况显然不可能. 那么A=ansj 我们不断向前寻找last[answ] 发现可以到达L 但是此时L的答案是ansi与最小不符故架设不成立. 还有一个更清晰的证明 显然的若存在这个w 那么我们ansi一定能将其覆盖 因为是这样的可以画图证明 所以说a能组成这个w 所以命题不成立. 所以原命题得证.实际上令最长匹配的前后缀长度为j,则Si的答案要么与Sj相等,要么是自己本身长度为i的串。

第一题 minigolf 描述很麻烦 其实就是想让我们确定一个对于每一个选手都确定一个A使这个选手获得一个最小的排名.
显然是暴力 枚举,之后发现了A具有一丝丝单峰的性质 在函数图像上具有单峰的性质 但可能存在在某一段区间之上值是相同的 所以三分不了..
显然二分没用 考虑一下分段 我们可以发现每个人在A值的不同时得分情况是一个分段函数 由于最多有50次比赛所以这个函数分段最多分50段.
进一步的 我们考虑逐人考虑答案 那么答案就是在这个分段函数的覆盖上的问题了 在某一段 很多人比其高就区间+1 什么的 最后取区间最大值即可.
显然 我们暴力求交也不过50*50这个复杂度再乘上人数?\(p^2\)罢了 另外值得一提的是对于求交我们可以O(50)扫即可.最后取区间最大值也很简单...

const int MAXN=510;
int n,m,cnt,tmp;
int a[MAXN][MAXN];
ll sum[MAXN];
inline int cmp1(int x,int y){return x>y;}
struct wy
{
	int x,op;
}t[MAXN*MAXN];
inline int cmp(wy a,wy b){return a.x==b.x?a.op>b.op:a.x>b.x;}
int main()
{
	freopen("minigolf.in","r",stdin);
	freopen("minigolf.out","w",stdout);
	n=read();m=read();
	for(int i=1;i<=n;++i)
	{
		for(int j=1;j<=m;++j)
		{
			a[i][j]=read();
			sum[i]+=a[i][j];
		}
		sort(a[i]+1,a[i]+2+m,cmp1);
	}
	for(int i=1;i<=n;++i)
	{
		cnt=0;tmp=0;
		for(int j=1;j<=n;++j)
		{
			ll si=sum[i],sj=sum[j],lim=INF;
			if(sj<=si)++tmp;
			int k=1,l=1;
			while(k<=m||l<=m)
			{
				ll tmpi=si,tmpj=sj;
				int mx=max(a[i][k],a[j][l]);
				si-=(lim-mx)*(k-1);
				sj-=(lim-mx)*(l-1);
				lim=mx;
				//求交点
				if(tmpj>tmpi&&sj<=si)t[++cnt]=(wy){lim+(si-sj)/(l-k),1};
				else if(tmpj<=tmpi&&sj>si)t[++cnt]=(wy){lim+(sj-si-1)/(k-l),-1};
				a[i][k]>a[j][l]?++k:++l;
			}
		}
		sort(t+1,t+1+cnt,cmp);
		int res=tmp;
		for(int j=1;j<=cnt;++j)
		{
			tmp+=t[j].op;
			res=min(res,tmp);
		}
		printf("%d\n",res);
	}
	return 0;
}

T2 先咕了 多项式 树形dp之类的不是很想写。

posted @ 2020-01-18 16:32  chdy  阅读(168)  评论(0编辑  收藏  举报