2019-10-30 CSP-S模拟测试

D2

T1

在这里插入图片描述
树状数组好题
妙处:

  1. 三维偏序根据一个来排序,一个做下标,一个插入值,降成了二维
  2. 离散化的妙处:把b的前缀和进行离散化处理,很方便的作为下标插入了树状数组

思路:

  1. a [ ] , b [ ] a[],b[] a[],b[]预处理成 ∑ a i , ∑ b i \sum{a_i},\sum{b_i} ai,bi,按照现在的a数组升序排序
  2. 离散化 b b b数组,在树状数组中查询比 b b b数组代表的数的当前 i d id id小的数中 i d id id最小的是哪个
  3. 将当前的数插入树状数组
#include <bits/stdc++.h>
using namespace std;
const int N=1000050;
inline long long read(){
	long long cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c&15);c=getchar();}
	return cnt*f;
}
const int inf=1e9;
typedef long long ll;
ll c[N];int siz,n;
struct node{ll a,b;int id;}x[N];
bool cmp(node A,node B){return A.a<B.a;}
struct BIT{
	int c[N];BIT(){memset(c,0x3f,sizeof(c));}
	void add(int x,int v){for(;x<=siz;x+=x&(-x))c[x]=min(c[x],v);}
	int ask(int x){int ans=inf;for(;x;x-=x&(-x))ans=min(ans,c[x]);return ans;}
}bit;
int main(){
	n=read();
	for(int i=1;i<=n;i++){x[i].a=x[i-1].a+read();x[i].id=i;}
	for(int i=1;i<=n;i++){x[i].b=read()+x[i-1].b;c[++siz]=x[i].b;}
	sort(x,x+1+n,cmp);sort(c+1,c+1+siz);siz=unique(c+1,c+1+n)-(c+1);int ans=0;
	for(int i=0;i<=n;i++){
		int p=lower_bound(c+1,c+siz+1,x[i].b)-c;
		ans=max(ans,x[i].id-bit.ask(p));
		bit.add(p,x[i].id);
	}
	printf("%d",ans);	return 0;
}

T2

在这里插入图片描述
思路:可以看到,当前的权值和是由一棵棵子树的权值和构成的,子树上的节点又是一段段连续的区间,于是我们就想到了区间DP。可以用四边形不等式证明此时决策是单调的这位blog写的太好了orz
f i , j f_{i,j} fi,j表示只考虑从节点 i i i j j j时我们得到的最优答案,则有:
f i , j = m i n { f i , k − 1 + f k + 1 , j } f_{i,j}=min\{f_{i,k-1}+f_{k+1,j}\} fi,j=min{fi,k1+fk+1,j}+ x [ i x[i x[i t o to to j ] j] j]
又因为我们已经证明了决策点是单调的,所以只需要在 [ i − 1 , j ] [i-1,j] [i1,j] [ i , j + 1 ] [i,j+1] [i,j+1]中枚举决策点即可,然后就把时间复杂度从 O ( n 3 ) O(n^3) O(n3)优化到了 O ( n 2 ) O(n^2) O(n2)

#include <cstdio>
#include <string>
using namespace std;
const int N=5050;
int g[N][N];
long long f[N][N],x[N],n;
inline long long read(){
	long long cnt=0,f=1;char c=getchar();
	while(!isdigit(c)){if(c=='-')	f=-f;c=getchar();}
	while(isdigit(c)){cnt=(cnt<<3)+(cnt<<1)+(c&15);c=getchar();}
	//上一段读入优化要把(c^48)改成(c&15)时间才卡的过去
	return cnt*f;
}
int main(){
	n=read();
	for(int i=1;i<=n;i++)
		x[i]=read(),x[i]+=x[i-1];
	for(int i=1;i<=n;i++)
		f[i][i]=x[i]-x[i-1],g[i][i]=i;
	register int j,i,l,k;
	for(k=1;k<n;k++){
		for(i=1;(j=i+k)<=n;i++){
			f[i][j]=1e18;
			for(l=g[i][j-1];l<=g[i+1][j];l++){
				if(f[i][l-1]+f[l+1][j]<f[i][j])
					f[i][j]=f[i][l-1]+f[l+1][j],g[i][j]=l;
			}
			f[i][j]+=x[j]-x[i-1];
		}
	}
	printf("%lld",f[1][n]);
	return 0;
}

T3

在这里插入图片描述

w a l k walk walk ( f r o m (from (from s o l u t i o n ) solution) solution)
我们首先考虑单个的 k k k。设 f i f_i fi表示从 i i i出发第一次到 k k k的期望步数,那么 f k = 0 , f i = ∑ ( f j ) x i + 1 f_k=0,f_i=\frac{\sum(f_j)}{x_i}+1 fk=0fi=xi(fj)+1 ( i ! = k i!=k i!=k j j j i i i的所有出边, x i x_i xi i i i的出度),用高斯消元解出 f 1 f_1 f1即可。
我们将每个点的方程先按第二种方式写出,那么对于每一个 k k k,我们只需要修改矩阵的一行。使用分治消元即可求出答案。
分治消元的具体做法是:先用前一半的方程进行消元,然后递归后一半;(恢复原矩阵后)再用后一半的方程进行消元,然后递归前一半。这样当区间缩小到单点时,这个方程并没有拿来消其它的方程,我们可以直接修改它并求出所有 f i f_i fi。 时间复杂度 O ( n 3 ) O(n^3) O(n3)

注:代码来自 s t d std std,非本人

#include <cstdio>
#include<iostream>
#define L long long
#define vi vector<int>
#define pb push_back
using namespace std;
const int q=998244353;
int n,m,f[310][310],g[10][310][310],x[310][310];
bool y[310][310];
inline int power(int a,int b)
{
    if(!b)
      return 1;
    int c=power(a,b>>1);
    c=(L)c*c%q;
    if(b&1)
      c=(L)c*a%q;
    return c;
}
inline void dfs(int l,int i)
{
    int j;
    x[l][i]=f[i][0];
    for(j=1;j<=n;j++)
      	if(j!=i && f[i][j])
        {
         	if(!y[l][j])
           	dfs(l,j);
         	x[l][i]=(x[l][i]-(L)f[i][j]*x[l][j])%q;
        }
    y[l][i]=1;
}
inline void calc(int l,int r,int p)
{
    int i,j,k,m=(l+r)>>1;
    if(l==r)
    {
       	y[l][l]=1;
       	for(i=1;i<=n;i++)
         	if(!y[l][i])
           		dfs(l,i);
       	return;
    }
    for(i=l;i<=r;i++)
    {
       	g[p][i][0]=f[i][0];
       	for(j=l;j<=r;j++)
         	g[p][i][j]=f[i][j];
    }
    for(i=l;i<=m;i++)
    {
    	k=power(f[i][i],q-2);
       	f[i][0]=(L)f[i][0]*k%q;
       	for(j=i;j<=r;j++)
	    f[i][j]=(L)f[i][j]*k%q;
	    for(j=i+1;j<=r;j++)
	        if(f[j][i])
	        {
	        	f[j][0]=(f[j][0]-(L)f[i][0]*f[j][i])%q;
	            for(k=r;k>=i;k--)
	            f[j][k]=(f[j][k]-(L)f[i][k]*f[j][i])%q;
	        }
    }
    calc(m+1,r,p+1);
    for(i=l;i<=r;i++)
      {
       f[i][0]=g[p][i][0];
       for(j=l;j<=r;j++)
         f[i][j]=g[p][i][j];
      }
    for(i=r;i>m;i--)
      {
       k=power(f[i][i],q-2);
       f[i][0]=(L)f[i][0]*k%q;
       for(j=l;j<=i;j++)
         f[i][j]=(L)f[i][j]*k%q;
       for(j=i-1;j>=l;j--)
         if(f[j][i])
           {
            f[j][0]=(f[j][0]-(L)f[i][0]*f[j][i])%q;
            for(k=l;k<=i;k++)
              f[j][k]=(f[j][k]-(L)f[i][k]*f[j][i])%q;
           }
      }
    calc(l,m,p+1);
    for(i=l;i<=r;i++)
      {
       f[i][0]=g[p][i][0];
       for(j=l;j<=r;j++)
         f[i][j]=g[p][i][j];
      }
}
int main()
{
    int i,j,k;
    scanf("%d%d",&n,&m);
    for(i=1;i<=m;i++)
      {
       scanf("%d%d",&j,&k);
       f[j][j]++;
       f[j][0]++;
       f[j][k]--;
      }
    calc(1,n,0);
    for(i=2;i<=n;i++)
      printf("%d\n",(x[i][1]+q)%q);
    return 0;
}

可参考FSY

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值