2019雅礼集训Day7

T1

一度以为考场上写不完…虽然最终杠完了
考虑两个位置 i , j i,j i,j上的数做完 k k k次操作后顺序是否变换的方案数
那么就是 d p [ i ] [ j ] [ k ] [ 0 / 1 ] dp[i][j][k][0/1] dp[i][j][k][0/1]
对于 n 3 k n^3k n3k的转移,枚举一下中间点是什么然后讨论
对于 n 2 k n^2k n2k的转移,我们分几种情况讨论
分别是区间覆盖了 [ i , j ] [i,j] [i,j]两个数,只覆盖了 i i i,只覆盖了 j j j,与什么都不覆盖的
我们注意到 [ i , j ] [i,j] [i,j]想要变为 [ x , y ] [x,y] [x,y],翻转区间的中心是确定的
只需要知道两边边界最多有多少
那么讨论边界转移即可…
有时间把代码扔上来吧…说可能说不清

upd:来噜

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#include<set>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair<long long,long long>
#define pii pair<int,int>
using namespace std;
inline int read()
{
	int f=1,x=0;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;
}
int stack[20];
inline void write(int x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
const int MAXN=501;
const int MAXK=51;
const int mod=1e9+7;

void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int a[MAXN],n,K;
int f[2][MAXN][MAXN][2];//0 不换位   1 换位 
int f1[MAXN][MAXN][2],f2[MAXN][MAXN][2];
int g1[MAXN][MAXN][2],g2[MAXN][MAXN][2];
int u1[MAXN][MAXN][2],u2[MAXN][MAXN][2];
int v1[MAXN][MAXN][2],v2[MAXN][MAXN][2];
//1 表示乘了i,2表示没乘i 
inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
inline void prenext(int nw)
{
//	memset(f1,0,sizeof(f1));memset(f2,0,sizeof(f2));
//	memset(g1,0,sizeof(f1));memset(g2,0,sizeof(f2));
//	memset(u1,0,sizeof(f1));memset(u2,0,sizeof(f2));
//	memset(v1,0,sizeof(f1));memset(v2,0,sizeof(f2));
	for(register int i=n;i>=1;--i)for(register int j=i+1;j<=n;++j)for(register int k=0;k<=1;++k)
	{
		int len=j-i;
		g1[i][len][k]=add(g1[i+1][len][k],1LL*f[nw][i][j][k]*i%mod);
		g2[i][len][k]=add(g2[i+1][len][k],f[nw][i][j][k]);
	}
	for(register int i=1;i<=n;++i)for(register int j=1;j<i;++j)for(register int k=0;k<=1;++k)
	{
		int len=i-j;
		f1[i][len][k]=add(f1[i-1][len][k],1LL*f[nw][j][i][k]*(n-i+1)%mod);
		f2[i][len][k]=add(f2[i-1][len][k],f[nw][j][i][k]);
	}
	for(register int i=1;i<=n;++i)for(register int j=i-1;j>=1;--j)for(register int k=0;k<=1;++k)
	{
		int len=i-j;
		u1[i][j][k]=add(u1[i][j+1][k],1LL*f[nw][j][i][k]*j%mod);
		u2[i][j][k]=add(u2[i][j+1][k],f[nw][j][i][k]);
	}
	for(register int i=1;i<=n;++i)for(register int j=n;j>i;--j)for(register int k=0;k<=1;++k)
	{
		int len=j-i;
		v1[i][j][k]=add(v1[i][j+1][k],1LL*f[nw][i][j][k]*j%mod);
		v2[i][j][k]=add(v2[i][j+1][k],f[nw][i][j][k]);
	}
}
inline int calc(int u)
{
	if(u<1)return 0;
	return 1LL*u*(u+1)/2%mod;
}
int pow_mod(int a,int b)
{
	int ret=1;
	while(b)
	{
		if(b&1)ret=1LL*ret*a%mod;
		a=1LL*a*a%mod;b>>=1;
	}
	return ret;
}
int main()
{
	freopen("inverse.in","r",stdin);
	freopen("inverse.out","w",stdout);
	n=read();K=read();
	for(register int i=1;i<=n;i++)a[i]=read();
	for(register int i=1;i<=n;++i)for(register int j=1;j<=n;++j)f[0][i][j][0]=1;
	prenext(0);int nw=0;
	for(register int T=1;T<=K;++T)
	{
		nw^=1;memset(f[nw],0,sizeof(f[nw]));
		for(register int i=1;i<=n;++i)for(register int j=i+1;j<=n;++j)for(register int k=0;k<=1;++k)
		{
			int len=j-i;
			//先弄区间在左边的
			int lim=n-j+1;
			if(lim<=i)ad(f[nw][i][j][k],1LL*(g2[lim][len][k^1]-g2[i+1][len][k^1]+mod)%mod*lim%mod);
			ad(f[nw][i][j][k],(g1[1][len][k^1]-g1[min(lim,i+1)][len][k^1]+mod)%mod);
			//再弄区间在右边的
			lim=i;
			if(n-lim+1>j)ad(f[nw][i][j][k],1LL*(f2[n-lim+1][len][k^1]-f2[j][len][k^1]+mod)%mod*lim%mod); 
			ad(f[nw][i][j][k],(f1[n][len][k^1]-f1[max(n-lim+1,j)][len][k^1]+mod)%mod);
			//弄只变了左边位置的 
			
			//在i左边(包括i 
			lim=j-i;
			if(lim<=i)ad(f[nw][i][j][k],1LL*(u2[j][lim][k]-u2[j][i+1][k]+mod)%mod*lim%mod);
			ad(f[nw][i][j][k],(u1[j][1][k]-u1[j][min(lim,i+1)][k]+mod)%mod);
			//在i右边 
			int o=j-1;
			lim=i;
			if(o-lim+1>i)ad(f[nw][i][j][k],1LL*(u2[j][i+1][k]-u2[j][o-lim+2][k]+mod)%mod*lim%mod);
			ad(f[nw][i][j][k],(1LL*u2[j][max(o-lim+2,i+1)][k]*(o+1)%mod-u1[j][max(o-lim+2,i+1)][k]+mod)%mod);
		
			//在j右边(包括j 
			lim=j-i;
			if(n-lim+1>=j)ad(f[nw][i][j][k],1LL*(v2[i][j][k]-v2[i][n-lim+2][k]+mod)%mod*lim%mod);
			ad(f[nw][i][j][k],(1LL*v2[i][max(n-lim+2,j)][k]*(n+1)%mod-v1[i][max(n-lim+2,j)][k]+mod)%mod);
			
			o=i+1;lim=n-j+1;
			if(o+lim-1<j)ad(f[nw][i][j][k],1LL*(v2[i][o+lim-1][k]-v2[i][j][k]+mod)%mod*lim%mod);
			ad(f[nw][i][j][k],((v1[i][i+1][k]-v1[i][min(o+lim-1,j)][k]+mod)%mod-1LL*(v2[i][i+1][k]-v2[i][min(o+lim-1,j)][k]+mod)%mod*(o-1)%mod+mod)%mod);
			
			ad(f[nw][i][j][k],1LL*f[nw^1][i][j][k]*(calc(i-1)+calc(j-i-1)+calc(n-j))%mod);
		}
		prenext(nw);
	}
	int ans=0;
	for(register int i=1;i<=n;++i)for(register int j=i+1;j<=n;++j)
	{
		if(a[i]<a[j])ad(ans,f[nw][i][j][1]);
		else ad(ans,f[nw][i][j][0]);
	}
	LL inv=pow_mod(1LL*n*(n+1)/2%mod,mod-2);
	inv=pow_mod(inv,K);
	pr2(1LL*ans*inv%mod);
	return 0;
}

**T2

打表题不会做…
朴素 f ( i , j ) f(i,j) f(i,j)表示前 i i i个数长度为 j j j的子序列最大值
转移就是 f ( i , j ) = m a x ( f ( i − 1 , j ) , f ( i − 1 , j − 1 ) + a [ i ] ∗ j ) f(i,j)=max(f(i-1,j),f(i-1,j-1)+a[i]*j) f(i,j)=max(f(i1,j),f(i1,j1)+a[i]j)
打表发现对于每个 i i i,取第二种转移的是连续一段后缀
那么splay维护dp值的差分
一次只需要插入一个 a [ i ] ∗ k a[i]*k a[i]k并且给后面的全部打上加 a [ i ] a[i] a[i]的标记

T3

误认为面积不能推下去所以…自闭了
一个显然的想法就是我们按逆时针扫点,这样可以去掉叉积的绝对值
我们还需要去掉面积差的绝对值,那就强行让选中的一片区域是较小的那个
那么一个点能选择编号最远的那个点是单调的
我们可以维护一段区间内的多边形面积和
容易发现,对于以 k k k号点为序号最大点的多边形,他少了的面积是三角形 p i , p i + 1 , p k p_i,p_{i+1},p_k pi,pi+1,pk的面积
推一下叉积,以下坐标以下标作为编号
( x i + 1 − x i ) ( y k − y i ) − ( x k − x i ) ∗ ( y i + 1 − y i ) (x_{i+1}-x_i)(y_k-y_i)-(x_k-x_i)*(y_{i+1}-y_i) (xi+1xi)(ykyi)(xkxi)(yi+1yi)
x i + 1 y k − x i + 1 y i − x i y k + x i y i − x k y i + 1 + x k y i − x i y i + x i y i + 1 x_{i+1}y_k-x_{i+1}y_i-x_iy_k+x_iy_i-x_ky_{i+1}+x_ky_i-x_iy_i+x_iy_{i+1} xi+1ykxi+1yixiyk+xiyixkyi+1+xkyixiyi+xiyi+1
y i ( x k − x i + 1 ) + y i + 1 ( x i − x k ) + y k ( x i + 1 − x i ) y_i(x_k-x_{i+1})+y_{i+1}(x_i-x_k)+y_k(x_{i+1}-x_i) yi(xkxi+1)+yi+1(xixk)+yk(xi+1xi)
可以前缀和优化了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值