AtCoder Regular Contest 100 题解

C:

直接将每个数-i,那么就是求中位数。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const LL inf=1LL<<60;
LL n,a[200010];
int main()
{
	scanf("%lld",&n);
	for(LL i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]-=i;
	sort(a+1,a+n+1);
	LL ans=0;
	for(LL i=1;i<=n;i++) ans+=abs(a[i]-a[n/2+1]);
	printf("%lld",ans);
	return 0;
}

D:

猜了个不会证的结论,枚举中间的刀,那么两边变成了子问题,显然两边最优时全局最优
容易发现两边的切点都是单调的,扫一遍即可。

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#define LL long long
using namespace std;
const LL inf=1LL<<60;
LL n,a[200010],sum[200010],ans=inf;
LL k1,k2,p;
void solve()
{
	LL t1=max(max(sum[k1],sum[p]-sum[k1]),max(sum[k2]-sum[p],sum[n]-sum[k2]));
	LL t2=min(min(sum[k1],sum[p]-sum[k1]),min(sum[k2]-sum[p],sum[n]-sum[k2]));
	ans=min(ans,t1-t2);
}
int main()
{
	scanf("%lld",&n);
	for(LL i=1;i<=n;i++) scanf("%lld",&a[i]);
	for(LL i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];
	k1=1;p=2;k2=3;
	while(k2<n-1&&abs(sum[k2]-sum[p]-(sum[n]-sum[k2]))>=abs(sum[k2+1]-sum[p]-(sum[n]-sum[k2+1]))) k2++;
	solve();
	while(p<n-2)
	{
		p++;if(k2==p) k2++;
		while(k1<p-1&&abs(sum[p]-sum[k1]-sum[k1])>=abs(sum[p]-sum[k1+1]-sum[k1+1])) k1++;
		while(k2<n-1&&abs(sum[k2]-sum[p]-(sum[n]-sum[k2]))>=abs(sum[k2+1]-sum[p]-(sum[n]-sum[k2+1]))) k2++;
		solve();
	}
	printf("%lld",ans);
}

E:

n 3 n^3 n3做法显然,状压F[S]表示S的子集中的最大值和次大值。
然后的sb否掉,写了可持久化trie 调了一下午
其实只要枚举数位,去掉1,就可以了。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
const int inf=1<<28;
int N=17;
struct node{
	int c1,k1,c2,k2;
	node() {c1=c2=-inf;}
};
struct trnode{
	int lc,rc;
	node c;
}tr[6000010];int root=0,tot=0;
int a[300010];
node operator + (node c1,node c2)
{
	node ans;
	if(c1.c1>c2.c1)
	{
		ans.c1=c1.c1;ans.k1=c1.k1;
		if(c1.c2>c2.c2) ans.c2=c1.c2,ans.k2=c1.k2;
		else ans.c2=c2.c2,ans.k2=c2.k2;
		if(c2.k1!=ans.k1&&c2.c1>ans.c2) ans.c2=c2.c1,ans.k2=c2.k1;
	}
	else
	{
		ans.c1=c2.c1;ans.k1=c2.k1;
		if(c1.c2>c2.c2) ans.c2=c1.c2,ans.k2=c1.k2;
		else ans.c2=c2.c2,ans.k2=c2.k2;
		if(c1.k1!=ans.k1&&c1.c1>ans.c2) ans.c2=c1.c1,ans.k2=c1.k1;
	}
	return ans;
}
int n;
void insert(int &x,int c,int d,int v,int num)
{
	if(!x) x=++tot;
	if(d==-1) {node t;t.c1=v;t.k1=num;t.c2=-inf;tr[x].c=tr[x].c+t;return;}
	int op=(c&(1<<d))?1:0;
	if(op) insert(tr[x].lc,c,d-1,v,num);
	else insert(tr[x].rc,c,d-1,v,num);
	tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c;
}
bool flag=false;
int merge(int x1,int x2,int d)
{
	int x=++tot;
	if(d!=-1) tr[x1].c=tr[tr[x1].lc].c+tr[tr[x1].rc].c;
	if(d!=-1) tr[x2].c=tr[tr[x2].lc].c+tr[tr[x2].rc].c;
	if(!x1||!x2) tr[x]=tr[x1+x2];
	else if(d==-1) tr[x].c=tr[x1].c+tr[x2].c;
	else
	{
		tr[x].lc=merge(tr[x1].lc,tr[x2].lc,d-1);
		tr[x].rc=merge(tr[x1].rc,tr[x2].rc,d-1);
		tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c;
	}
	return x;
}
void dfs(int x,int d)
{
	if(!x||d==-1) return;
	dfs(tr[x].lc,d-1);dfs(tr[x].rc,d-1);
	tr[x].lc=merge(tr[x].lc,tr[x].rc,d-1);
	tr[x].c=tr[tr[x].lc].c+tr[tr[x].rc].c;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<1<<n;i++) scanf("%d",&a[i]),insert(root,i,N,a[i],i);
	dfs(root,N);
	for(int i=1;i<(1<<n)-1;i++)
	{
		int x=root,ans=-inf,k=i+1;
		for(int j=N;j>=0;j--)
		{
			int c=(k&(1<<j))?1:0;
			if(c) ans=max(ans,tr[tr[x].rc].c.c1+tr[tr[x].rc].c.c2),x=tr[x].lc;
			else x=tr[x].rc;
		}
		printf("%d\n",ans);
	}
	printf("%d",tr[root].c.c1+tr[root].c.c2);
}

F:

目前为止做过最简单的F
先考虑怎么计算好串,感觉算不好串好写些,直接dp f [ i ] [ j ] f[i][j] f[i][j]表示前i位最后j位互不相同。
然后要求这些串包含 A A A,枚举A出现的位置,然后再计数。
假如 A A A中出现了长度为 K K K的互不相同子串, a n s = ( n − m + 1 ) ∗ k m − n ans=(n-m+1)*k^{m-n} ans=(nm+1)kmn
假如 A A A中有重复的数字,那么前后互不影响,分别求出两边合并即可。
最后一种情况因为前后互相影响,所以不能分开算。因为元素之间是没有区别的,那么我们可以先求出在所有不好的序列中,有多少个长度为m的子串满足其中元素两两不同,这个用上面的dp改改就好了,最后除以 k ! ( k − m ) ! \frac{k!}{(k-m)!} (km)!k!

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
const LL mod=1e9+7;
LL pow(LL a,LL b)
{
	LL ans=1;
	while(b)
	{
		if(b&1) ans=(LL)ans*a%mod;
		a=(LL)a*a%mod;b>>=1;
	}
	return ans;
}
LL n,k,m,a[25010],last[410],p[25010],l[25010];
LL ans=0,f[2][25010][410],sum[410],fac[410],inv[410];
void dp(LL op,LL len)
{
	f[op][0][len]=1;
	for(LL i=1;i<=n;i++)
	{
		sum[0]=0;for(LL j=1;j<k;j++) sum[j]=(sum[j-1]+f[op][i-1][j])%mod;
		for(LL j=1;j<k;j++) f[op][i][j]=((sum[k-1]-sum[j-1])%mod+f[op][i-1][j-1]*(k-j+1)%mod)%mod;
	}
}
void pre()
{
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for(LL i=2;i<=400;i++) fac[i]=(LL)fac[i-1]*i%mod,inv[i]=pow(fac[i],mod-2);
}
int main()
{
	pre();
	scanf("%lld %lld %lld",&n,&k,&m);
	for(LL i=1;i<=m;i++)
	{
		scanf("%lld",&a[i]);
		p[i]=last[a[i]];last[a[i]]=i;
	}
	LL t=0;bool flag=false;
	for(LL i=1;i<=m;i++)
	{
		t=max(t,p[i]+1);
		l[i]=t;if(i-l[i]+1==k) flag=true;
	}
	if(flag)
	{
		for(LL i=0;i<=n-m;i++) (ans+=(LL)pow(k,i)*pow(k,n-m-i)%mod)%=mod;
		printf("%lld",ans);return 0;
	}
	for(LL i=1;i<=m;i++) if(l[i]==1) t=i;
	if(t==m)
	{
		dp(0,0);
		for(LL i=1;i<=n;i++)
		{
			sum[0]=0;for(LL j=1;j<k;j++) sum[j]=(sum[j-1]+f[1][i-1][j])%mod;
			for(LL j=1;j<k;j++)
			{
				f[1][i][j]=((sum[k-1]-sum[j-1])%mod+f[1][i-1][j-1]*(k-j+1)%mod)%mod;
				if(j>=m) (f[1][i][j]+=f[0][i][j])%=mod;
			}
			//sum[0]=0;for(LL j=1;j<k;j++) sum[j]=((sum[j-1]+f[1][i-1][j])%mod+f[0][i-1][j])%mod;
			//for(LL j=m;j<k;j++) f[1][i][j]=((sum[k-1]-sum[j-1])%mod+(f[1][i-1][j-1]+f[0][i-1][j-1]*(k-j+1)%mod)%mod)%mod;
		}
		LL ans=0,tot=(LL)(n-m+1)*pow(k,n-m)%mod;
		for(LL i=1;i<k;i++) (ans+=(LL)f[1][n][i])%=mod;
		ans=(LL)ans*fac[k-m]%mod*inv[k]%mod;
		(tot-=ans)%=mod;
		printf("%lld",(tot+mod)%mod);
		return 0;
	}
	dp(0,t);dp(1,m-l[m]+1);
	for(LL i=0;i<=n-m;i++)
	{
		t=(LL)pow(k,i)*pow(k,n-m-i)%mod;
		LL k1=0,k2=0;
		for(LL j=1;j<k;j++) (k1+=f[0][i][j])%=mod,(k2+=f[1][n-m-i][j])%=mod;
		(ans+=(t-(LL)k1*k2%mod)%mod)%=mod;
	}
	printf("%lld",(ans+mod)%mod);	
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值