2019.01.28【NOIP提高组】模拟 B 组

日渐消瘦

  • JZOJ 4235 序列
    • 题目
    • 分析
  • JZOJ 4226 A
    • 题目
    • 分析
  • 代码
  • JZOJ 4227 B
    • 题目
    • 分析
    • 代码
  • JZOJ 4228 C
    • 题目
    • 分析
    • 代码
  • 后续

JZOJ 4235 序列

题目

n个数中找三个最靠前的数,使它们可以组成一个三角形,这n个数可能会被修改


分析

那么首先很容易想到暴力的方法,没错,这就是暴力,为什么呢,可以想到三角形之和大于第三边,如果要使它不合法且数值最小,那应该是一串斐波那契数列,问题是 f [ 50 ] f[50] f[50]就炸了,所以单次询问的时间复杂度为 O ( 5 0 3 ) O(50^3) O(503),题目过水,代码就不贴了


JZOJ 4226 A

题目

一个 n n n个点, m m m条边的无向图,现在增加最少的边(不能是自环,但可以是重边),使每个点的度至少为 k k k


分析

那么想到的最水的方法,就是求 ⌈ ( ∑ i = 1 , k > d e g [ i ] n k − d e g [ i ] ) ÷ 2 ⌉ \lceil(\sum_{i=1,k>deg[i]}^nk-deg[i])\div 2\rceil (i=1,k>deg[i]nkdeg[i])÷2
问题是这样会有问题(原谅我考试时没有找到可以卡掉它的数据)
2019.01.28【NOIP提高组】模拟 B 组_第1张图片
如图所示, k k k为3,那么5是完全被孤立的,理论上应该是 ⌈ 3 2 ⌉ = 2 \lceil\frac{3}{2}\rceil=2 23=2,但是应该是3,所以说还要加一个特判,也就是说如果需求量最大的点超过其它点的总和,那么答案就是需求量最大的点


代码

#include 
#include 
#define rr register
using namespace std;
int n,m,k,deg[100001];
long long ans;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
signed main(){
	n=iut(); m=iut()<<1; k=iut();
	while (m--) ++deg[iut()];
	for (rr int i=1;i<=n;++i)
	if (deg[i]<k) ans+=k-deg[i];
	for (rr int i=1;i<=n;++i)
	if (k-deg[i]>=((ans+1)>>1))
	    return !printf("%lld\n",k-deg[i]); 
	return !printf("%lld",(ans+1)>>1);
} 

JZOJ 4227 B

题目

给定一个长度为 n n n的数组 A A A,以及一个 n × 26 n×26 n×26 的矩阵 w w w,其中 w i , j w_{i, j} wi,j表示第 i i i个位置填第 j j j个小写字母的价值,现在你需要给出一个长度为 n n n的字符串,使得它的后缀数组是 A A A,而且它每个位置的价值和最大


分析

首先可以知道通过它们的排列可以知道它们在原串中的位置,接着就是一道dp题了
f [ i ] [ j ] f[i][j] f[i][j]表示在原串中位于位置 i i i,所填字母为 j j j的最大价值和,那么 f [ i ] [ j ] = f [ i − 1 ] [ k ] + w [ a [ i ] ] [ j ] f[i][j]=f[i-1][k]+w[a[i]][j] f[i][j]=f[i1][k]+w[a[i]][j]
特判:举个例子 a b a b a ababa ababa是可以用相同的字母,那应该怎么办呢,那么 r a n k [ a [ i ] + 1 ] > = r a n k [ a [ i − 1 ] + 1 ] rank[a[i]+1]>=rank[a[i-1]+1] rank[a[i]+1]>=rank[a[i1]+1]才可以保证合法,为什么呢, s [ i ] = s [ i − 1 ] s[i]=s[i-1] s[i]=s[i1],那么只要往后移一位比较就可以了


代码

#include 
#include 
#include 
#define rr register
using namespace std;
int n,rk[100001],a[100001],w[100001][28],f[2][28],ans; long long ran;
inline signed iut(){
	rr int ans=0; rr char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans;
}
inline signed rando(){
	ran=(ran*100000005+1532777326)%998244353;
	return ran/100;
}
inline signed max(int a,int b){return a>b?a:b;}
signed main(){
	n=iut(); scanf("%lld",&ran);
	for (rr int i=1;i<=n;++i) rk[a[i]=iut()]=i;
	for (rr int i=1;i<=n;++i)
	for (rr int j=1;j<27;++j) w[i][j]=rando()%10000;
	for (rr int i=1;i<27;++i) f[1][i]=w[a[1]][i];
	for (rr int i=2;i<=n;++i){
	    memset(f[i&1],0,sizeof(f[i&1]));
	    for (rr int j=1;j<27;++j)
	    for (rr int k=1;k<=j;++k){
		    if (j==k&&rk[a[i]+1]<rk[a[i-1]+1]) continue;
		    f[i&1][j]=max(f[i&1][j],f[1-(i&1)][k]+w[a[i]][j]);
        }
	}
	for (rr int i=1;i<27;++i) ans=max(ans,f[n&1][i]);
	return !printf("%d",ans);
}

JZOJ 4228 C

题目

n n n个点,每个点向上下左右四个方向之一连出一条射线,这些射线不能相交且射线不能经过除了发出点之外的其他点,问有多少种方案


分析

这是一道神奇的题目,设 f [ i ] [ a ] [ b ] [ c ] [ d ] f[i][a][b][c][d] f[i][a][b][c][d]表示第 i i i个点, a , b , c , d a,b,c,d a,b,c,d分别为
在这里插入图片描述
然后只要注意细节就行了


代码

#include 
#include 
#include 
#include 
#define rr register
#define g(i,a,b) for (rr int i=a;i<=b;++i)
using namespace std;
const int mod=998244353; int f[2][55][55][55][55],n;
struct site{int x,y;}t[55]; bool v[55][4];
inline signed iut(){
	rr int ans=0,f=1; rr char c=getchar();
	while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
	return ans*f;
}
bool cmp(site a,site b){return a.x<b.x||(a.x==b.x&&a.y<b.y);}
inline void add(int &a,int b){a=(a+b)%mod;}
signed main(){
	n=iut(); g(i,1,n) t[i]=(site){iut(),iut()};
	sort(t+1,t+1+n,cmp); f[0][0][0][0][0]=1;
	g(i,1,n){
		v[i][0]=v[i][1]=v[i][2]=v[i][3]=1;
		g(j,1,n) if (i!=j){
			if (t[i].y==t[j].y&&t[i].x>t[j].x) v[i][0]=0;
			if (t[i].y==t[j].y&&t[i].x<t[j].x) v[i][2]=0;
			if (t[i].x==t[j].x&&t[i].y<t[j].y) v[i][1]=0;
			if (t[i].x==t[j].x&&t[i].y>t[j].y) v[i][3]=0;
		}
	}
	g(i,1,n){
		g(a,0,i-1) g(b,0,i-1) g(c,0,i-1) g(d,0,i-1)
		    if (f[1-(i&1)][a][b][c][d]) g(j,0,3){
			if (!j){
				if (!v[i][0]) continue;
				if ((!c||t[c].y>t[i].y)&&(!d||t[d].y<t[i].y))
					add(f[i&1][a][b][c][d],f[1-(i&1)][a][b][c][d]);
		    }
		    else if (j==1){
		    	if (!v[i][1]) continue;
		    	if (!b||t[b].y<t[i].y){
		    		rr int xa=a,xb=b,xc=c,xd=d;
		    		if (!c||t[c].y>t[i].y) xc=i;
		    		add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
				}
			}
			else if (j==2){
				if (!v[i][2]) continue;
				rr int xa=a,xb=b,xc=c,xd=d;
				if (!a||t[i].y<t[a].y) xa=i;
				if (!b||t[i].y>t[b].y) xb=i;
				add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
			}
			else if (v[i][3]){
				if (!a||t[a].y>t[i].y){
					rr int xa=a,xb=b,xc=c,xd=d;
					if (!d||t[d].y<t[i].y) xd=i;
					add(f[i&1][xa][xb][xc][xd],f[1-(i&1)][a][b][c][d]);
				}
			}
		}
		memset(f[1-(i&1)],0,sizeof(f[1-(i&1)]));
	}
	rr int ans=0;
	g(a,0,n) g(b,0,n) g(c,0,n) g(d,0,n)
	    add(ans,f[n&1][a][b][c][d]);
	return !printf("%d",ans);
}

后续

日渐变菜

你可能感兴趣的:(模拟赛,模拟,搜索,线性dp)