20180804的Test

T1 偏差

【题目描述】

给出一个序列A,再给出一段序列B,对于1≤i≤m,可能有B[i]=A[L+i-1]+k,我们称k为这个偏差值,有5个询问:

  1. 偏差值k有几种可能
  2. 偏差值|k|的最小值是多少
  3. L有几种取值方案
  4. L的最小值是多少
  5. L的最大值是多少

【输入格式】

多组数据
第一行一个正整数 n,表示序列 A 的长度。
第二行 n 个用空格隔开的非负整数 A[1] . . . A[n],描述了序列 A。
第三行一个正整数 m,表示序列 B 的长度。
第四行 m 个用空格隔开的非负整数 B[1] . . . B[m],描述了序列 B。

【输出格式】

对于每组数据,输出 5 个用空格隔开的整数,依次表示 5 个问题的答案。特别地,对于问题 2, 4, 5,如果无解,请输出 0 作为答案。

【数据范围与约定】

\(1 \leq n , m \leq 10^5\)


题解

有了这个k的存在,让我们很头疼。要是没有这个k该多好,来一个KMP就可以轻松A掉了!因此我们需要把这个k给消掉,你只需要动用你聪明的小脑瓜,使用“瞪眼法”,就会有这一个想法:我们可不可以将A和B序列中相邻的两个数相减,那么这样k的影响就被消去了。此时在来一个KMP就可以搞定啦~~显然这是对的。那么我们就开始码代码了

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int N=1000005;
const int INF=2*1e9+7;
int n,m,a[N],b[N],p[N],x[N],y[N],x1,x2=INF,x3,x4=INF,x5=-INF,n1,tb[N];
int main()
{
	int T;
	scanf("%d",&T);
	while (T--){
		scanf("%d",&n);
		memset(p,0,sizeof(p)); x1=0; x2=INF; x3=0; x4=INF; x5=-INF; n1=0;
		for (int i=1; i<=n; i++) scanf("%d",&a[i]); n--;
		for (int i=1; i<=n; i++) x[i]=a[i+1]-a[i];
		scanf("%d",&m);
		for (int i=1; i<=m; i++) scanf("%d",&b[i]); m--;
		for (int i=1; i<=m; i++) y[i]=b[i+1]-b[i];
		if (m!=0){
			p[1]=0; int j=0;
			for (int i=1; i<m; i++){
				while (j>0 && y[j+1]!=y[i+1]) j=p[j];
				if (y[j+1]==y[i+1]) j++;
				p[i+1]=j;
			}
			j=0;
			for (int i=0; i<n; i++){
				while (j>0 && y[j+1]!=x[i+1]) j=p[j];
				if (y[j+1]==x[i+1]) j++;
				if (j==m){
					j=p[j];
					tb[++n1]=a[i-m+2]-b[1];
					x2=min(x2,abs(a[i-m+2]-b[1]));
					x3++; x4=min(x4,i-m+2); x5=max(x5,i-m+2);
				}
			}
			sort(tb+1,tb+1+n1);
			x1=unique(tb+1,tb+1+n1)-tb-1;
			if (x2==INF) x2=0; if (x4==INF) x4=0; if (x5==-INF) x5=0;
			printf("%d %d %d %d %d\n",x1,x2,x3,x4,x5);	
		}
		else {
			m++; n++;
			for (int i=1; i<=n; i++) x2=min(x2,abs(b[1]-a[i]));
			x3=n; x4=1; x5=n;
			sort(a+1,a+1+n);
			x1=unique(a+1,a+1+n)-a-1;
			printf("%d %d %d %d %d\n",x1,x2,x3,x4,x5);	
		}
	}
	return 0;
}

T2 闪烁魔法

【题目描述】

给一颗树,有边权,有k个插有旗帜的点,需要访问所有插有旗帜的点,且要回到出发点(出发点任选),而且每次访问都是最优的方案,插旗帜的方案是等概率的,求可能情况代价的平均值

【输入格式】

本题包含多组数据,第一行一个正整数 T,表示数据组数。接下来依次描述每组数据,对于每组数据:
第一行 2 个正整数 n, k,分别表示点数,以及插有旗帜的地点的数目。
接下来 n − 1 行,每行 3 个正整数 u,v,w,表示一条边,连接u,v,边权为w

【输出格式】

对于每组数据,输出一行一个正整数,表示期望消耗的魔法值在模998244353 意义下的结果。

【数据范围与约定】

$ 1\leq k\leq m\leq 10^5 $


题解

若是考虑每一种插旗情况,那实在有点多啊--||,肯定会TLE,因此我们要换一个思路,考虑每一条边。这条边被用到,当且仅当左边的子树有旗帜且右边的子树也有旗帜,故这条边对答案的贡献为:(a代表这条边左子树的点的个数)$ w_i*\sumk(C_aiC_{n-a}^{k-i})\( 但是这个方法的时间复杂度仍然不理想,我们考虑正难则反,可容易推得公式:\) w_i(C_nk-C_ak-C_{n-a}^k)$
接下来就可以愉快的码代码啦~~~

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#define N 300005
#define int long long
using namespace std;
const int mod=998244353;
int n,m,ans,sum[N],head[N],jc[N],jc_inv[N],edgenum=0,vet[N],dis[N],next[N];
void addedge(int u,int v,int t)
{
	vet[++edgenum]=v;
	next[edgenum]=head[u];
	head[u]=edgenum;
	dis[edgenum]=t;
}
int inv(int x)
{
	if (x==1) return 1;
	return (mod-mod/x)*inv(mod%x)%mod;
}
void init(int n)
{
	memset(head,-1,sizeof head); edgenum=ans=0;
	jc[0]=1;
	for (int i=1; i<=n; i++) jc[i]=jc[i-1]*i%mod;
	jc_inv[n]=inv(jc[n]);
	for (int i=n-1; i>=0; i--) jc_inv[i]=jc_inv[i+1]*(i+1)%mod;
}
int C(int x,int y)
{
	if (x<y) return 0;
	return jc[x]*jc_inv[y]%mod*jc_inv[x-y]%mod;
}
void calc(int k,int x,int y)
{
	int tmp=((C(n,m)-C(x,m)-C(y,m))%mod+mod)%mod;
	ans=(ans+tmp*k)%mod;
}
void dfs(int u,int fa)
{
	sum[u]=1;
	for (int e=head[u],v; e!=-1; e=next[e])
		if ((v=vet[e])!=fa){
			dfs(v,u); sum[u]+=sum[v];
			calc(dis[e],sum[v],n-sum[v]);
		}
}
signed main(){
	int T;
	scanf("%lld",&T);
	while (T--){
		scanf("%lld%lld",&n,&m);
		init(n);
		for (int i=1; i<n; i++){
			int x,y,z;
			scanf("%lld%lld%lld",&x,&y,&z);
			addedge(x,y,z); addedge(y,x,z);
		}
		dfs(1,0);
		ans=ans*2%mod*inv(C(n,m))%mod;
		printf("%lld\n",ans);
	}
	return 0;
}	

T3 景中人

【题目描述】

给n个整点(横纵坐标均为非负整数),要用矩形把其都覆盖住(确定矩形面积),且每个矩形要贴着直线\(y=0\),求至少需要几个矩形

【输入格式】

本题包含多组数据。第一行一个整数 T,表示数据组数。接下来依次描述各组数据,
对于每组数据:
第一行 2 个整数 n, S,表示n个点,矩形面积必须为S。
接下来 n 行,每行 2 个非负整数 x, y,描述每个点的横纵坐标。

【输出格式】

对于每组数据,一行一个整数表示所需要使用的最少的矩形数目。

【数据范围与约定】

\(n\leq 100 ,x\leq3*10^6 , 1\leq y,S\leq2*10^5\)


题解

先把坐标离散化。
首先得知道一个结论:最优解中任意两个矩形的横坐标只可能是相离或包含,不可能是相交。证明略。
显然区间DP是个不错的选择。
\(f_{l,r,h}\)为覆盖横坐标\(l∼r\),纵坐标>h的所有矩形需要的最少次数。
枚举l,r,h,有两种转移:找到一个横坐标i,使得没有任意一个矩形穿过i。枚举i分治即可。放一个横坐标为\(l∼r\)的矩形,把高度设为上限。
对于每一个h,这一层的转移是\(O(n^3)\)的,到下一层的转移是\(O(n^2\log n)\) 的,所以总时间复杂度就是\(O(n^4)\)

代码

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<cstdlib>
using namespace std;
const int INF=2*1e9+7;
const int N=105;
struct node {int x,y;}a[N];
int n,s,f[N][N][N],tx[N],ty[N],n1,n2,id[N];
int calc(int x)
{
	if (x) return s/x;
    return INF;
}
int dfs(int l,int r,int h)
{
    int &s=f[h][l][r];
    if(~s) return s;
    while(l<=r && id[l]<=h) l++;
    while(l<=r && id[r]<=h) r--;
    if (l>r) return s=0;
    s=INF;
    for(int i=l; i<r; i++) s=min(s,dfs(l,i,h)+dfs(i+1,r,h));
    int hh=calc(tx[r]-tx[l]);
    if (hh<=ty[h]) return s;
    int v=upper_bound(ty+1,ty+n2+1,hh)-ty-1;
    s=min(s,dfs(l,r,v)+1);
    return s; 
}
int main()
{
    int T;
    scanf("%d",&T);
    while (T--) {
	    scanf("%d%d",&n,&s);
	    for(int i=1; i<=n; i++){
		    scanf("%d%d",&a[i].x,&a[i].y);
		    tx[i]=a[i].x; ty[i]=a[i].y;
		}
	    sort(tx+1,tx+n+1); sort(ty+1,ty+n+1);
	    n1=unique(tx+1,tx+n+1)-tx-1; n2=unique(ty+1,ty+n+1)-ty-1;
	    memset(f,-1,sizeof f);
	    for(int i=1; i<=n1; i++) id[i]=0;
	    for(int i=1; i<=n; i++){
		    a[i].x=lower_bound(tx+1,tx+n1+1,a[i].x)-tx;
		    a[i].y=lower_bound(ty+1,ty+n2+1,a[i].y)-ty;
		    id[a[i].x]=max(id[a[i].x],a[i].y);
		}
	    int ans=dfs(1,n1,0);
	    printf("%d\n",ans);
	}
    return 0;
}

总结:

今天LOJ大吉,洛谷大凶,真是矛盾啊...为我脸黑埋伏笔这里写图片描述
这里写图片描述
这估计也是注定了我要么A掉,要么爆零的脸黑结果吧
这套题,说难不难,说简单不简单,镇中大佬集体RP爆降是我排名虚高的原因。不能因此而嘚瑟

T1 100/100

这道题,看题3min就出正解,虽说KMP稍有忘却,但还是可以1h AC,稳!!

T2 0/100

这道题,暴力居然WA了,如此脸黑的我也很无奈啊╮(╯▽╰)╭,后面急于写正解,也没有时间去过多关注如何WA的,估计是手抖吧,但是没有想到基于边的算法,这说明思路过于局限,不过之前也从未做过此类题,情有可原。

T3 0/100

唔,错误贪心,我也是走投无路啊,曾经想到过DP(一闪而现),但是思路被我一秒枪毙(感觉不对+写不出来)...终究还是只能归结于太嫩了,题目做得太少。

心得:

题目还是要多做啊,拓宽视野,算法的不足也要及时补上,加油吧。
正因为生命有限,所以才显得更重要,正因为生命有限,所以才更应该努力不懈。

posted @ 2018-08-05 08:23  wangyh1008  阅读(162)  评论(0编辑  收藏  举报