【比赛报告】2018.08.06集训 NOIP练习赛卷九

A.哥德巴赫矩阵

题目链接

问题描述

根据哥德巴赫猜想(每个不小于 6 的偶数都可以表示为两个奇素数之和),定义
哥德巴赫矩阵 A 如下:对于正整数对(i,j),若 i+j 为偶数且 i,j 均为奇素数,则 Ai,j = 1,
否则 Ai,j = 0。现在有若干询问(x1,y1,x2,y2),你需要回答下列式子的值
这里写图片描述

输入

第一行一个整数 m
接下来 m 行,每行四个整数 x1 y1 x2 y2,表示一个询问

输出

m 行,每行一个整数,表示对应询问的答案

输入样例

1
1 1 3 5

输出样例

2

数据规模与约定

30%的数据保证 x2, y2, m ≤ 100
100%的数据保证 1 ≤ x1 ≤ x2 ≤ 106; 1 ≤ y1 ≤ y2 ≤ 106; m ≤ 1000


题解

比赛时没有预处理前缀和,也没开long long(比赛经验太差)

#include<cstdio>
#include<cstring>
typedef long long ll;
const int N=1e6;
int prime[N+10],p=0;
bool iscomp[N+10];
void primetable()
{
	for(int i=2;i<=N;i++)
	{
		if(!iscomp[i])prime[p++]=i;
		for(int j=0;j<p&&i*prime[j]<=N;j++)
		{
			iscomp[i*prime[j]]=1;
			if(i%prime[j]==0)break;
		}
	}
}
ll sum[N+10];
void Init()
{
	for(int i=1;i<=N;i++)
	{
		if(i==1||i==2||iscomp[i])sum[i]=sum[i-1];
		else sum[i]=sum[i-1]+1;
	}
}
int main()
{
	//freopen("pmatrix.in","r",stdin);
	//freopen("pmatrix.out","w",stdout);
	int m,x1,y1,x2,y2,i,j;
	ll ans;
	scanf("%d",&m);
	memset(iscomp,0,sizeof(iscomp));
	primetable();
	Init();
	for(int k=1;k<=m;k++)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		ans=(sum[x2]-sum[x1-1])*(sum[y2]-sum[y1-1]);
		printf("%lld\n",ans);
	}
	return 0;
}

总结

感觉是这次比赛唯一能做的题……错误的认为应该跑得过,于是没有用前缀和;也没有想过会不会爆int
主要反映出比赛经验的严重不足


B.表格

题目链接

问题描述

给出一个表格, N 行 M 列,每个格子有一个整数,有些格子是空的。现在需要你
来做出一些调整,使得每行都是非降序的。这个调整只能是整列的移动。

输入

第一行两个正整数 N 和 M。
接下来 N 行,每行 M 个整数, -1 表示这个格子是空的,其他的整数都在 [0, 10^9]范围,表
示格子的数字。

输出

若无解,输出 -1;
否则输出任意一个解,即一行 M 个正整数 p1, p2, · · · , pm,表示可以把初始表格的 pi 列,
放在新表格的第 i 列,以得到一个合法的表格。

样例输入 1

3 3
-1 -1 -1
2 1 2
2 -1 1

样例输出 1

2 3 1

样例输入 2

2 2
1 2
2 1

样例输出 2

1

数据规模与约定

对于 20% 的数据,满足 1 ≤ N ≤ 8, 1 ≤ M ≤ 8。
对于 60% 的数据,满足 1 ≤ N × M ≤ 2 × 10^3。
对于 100% 的数据,满足 1 ≤ N × M ≤ 10^5。


题解

对每一列小到大排序,忽略-1 的。排前面的列,向后面的建边。因为有多个相同数字的,
所以建一些虚点。

2 2 3 3
在 2,3 之间建一个虚点, 2 2 向虚点建边,虚点向 3 3 建边
最后跑拓扑排序

#include<cstdio>
#include<vector>
#include<algorithm>
#include<queue>
#define PB(v) push_back(v)
using namespace std;
const int N=5e5+10;
int n,m;//n行m列 
struct node{
	int num,idy;//数值和初始列序号 
	bool operator <(const node&rhs)const{
	return num<rhs.num;}
}table[N];
vector<int>vx[N];//建图用
queue<int>q;//存所有入度为0的点 
int deg[N];//单行中每列的入度 
int ans[N];//记录可行解 
int main()
{
	freopen("in.txt","r",stdin);
	scanf("%d%d",&n,&m);//n行m列
	int xd=m;//虚点下标 
	for(int i=0;i<n;i++)
	{
		for(int j=0;j<m;j++)//读入1行中每列的数值 
		{
			scanf("%d",&table[j].num); 
			table[j].idy=j;
		}
		sort(table,table+m);//对一行的值进行排序 
		for(int k=-1,l=0,r=0;l<m;l=r)
		{
			//k为建立的虚点,l为相同区间左端点,r为右端点
			while(r<m&&table[r].num==table[l].num)r++;
			if(table[l].num==-1)continue;
			if(k!=-1)for(int o=l;o<r;o++)vx[k].PB(table[o].idy),deg[table[o].idy]++;
		    //虚点已经建立,则虚点向后连边
			k=xd++;//新建虚点
			for(int o=l;o<r;o++)vx[table[o].idy].PB(k),deg[k]++;//将之前的点向虚点连边 
		}
	}
	int top=0;
	for(int i=0;i<xd;i++)
	if(!deg[i])q.push(i);
	while(!q.empty())//拓扑排序 
	{
		int u=q.front();
		q.pop();
		if(u<m)ans[top++]=u;
		for(int i=0;i<vx[u].size();i++)
		{
			int v=vx[u][i];
			if(--deg[v]==0)q.push(v);
		}
	} 
	if(top<m)puts("-1");
	else for(int i=0;i<m;i++)printf("%d%c",ans[i]+1,i==m-1?'\n':' ');
	return 0;
}

总结

比赛时完全没思路……对拓扑排序很不熟
比赛时拿到这题完全不知道怎么做,严格升序与不严格升序看得头大。
反映出对于拓扑排序这个知识点的模糊
trick:通过建虚点优化


C.看电影

题目链接

问题描述

小石头喜欢看电影,选择有 N 部电影可供选择,每一部电影会在一天的不同时段播
放。他希望连续看 L 分钟的电影。因为电影院是他家开的,所以他可以在一部电影播放过
程中任何时间进入或退出,当然他不希望重复看一部电影,所以每部电影他最多看一次,
也不能在看一部电影的时候,换到另一个正在播放一样电影的放映厅。
请你帮助小石头让他重 0 到 L 连续不断的看电影,如果可以的话,计算出最少看几
部电影。

输入格式

第一行是 2 个整数 N,L,表示电影的数量,和小石头希望看的连续时间
接下来是 N 行,每行第一个整数 D(1<=D<=L)表示电影播放一次的播放时间,第二个整数
是 C 表示这部电影有 C 次播放,接下来是 C 个整数表示 C 次播放的开始时间 Ti
(0<=Ti<=L) ,Ti 是按升序给出。

输出格式

一个整数,表示小石头最少看的电影数量,如果不能完成输出-1

输入样例

4 100
50 3 15 30 55
40 2 0 65
30 2 20 90
20 1 0

输出样例

3

样例说明

开始他选择最后一步电影从 0 时间开始。
到了 20 分钟,他选择第一部电影的第一次播放,看到 65 分钟
最后他选择第二部电影的第二次播放,从 65 分钟到 100 分钟

数据规模

30%数据 N<=10
100%数据 N<=20, 1 <= L <= 100,000,000


题解

状压 DP, f[i]表示状态为 i 时从 0 最远连续看到哪。
然后转移时候, 枚举还要看哪部电影,贪心取能看的片场中最靠后的一个。
然后时间复杂度 O(2^N×N×x)
其中 x 是求能看片场中最靠后一个的时间复杂度。
求法 1:
二分。 x=log2C
求法 2:
类似单调队列预处理,然后 x=1

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define INF 0x3f3f3f3f
const int N=25;
int n,l;
int S;
int len[N],movie[N][1010];
int f[1<<N];//f[i]表示状态为i时能看的最长时间 
int p[N];
int find(int x,int id)
{
	int l=-1,r=p[id]-1,mid;
	while(l<r)
	{
		mid=(l+r+1)>>1;
		if(movie[id][mid]<=x)l=mid;
		else r=mid-1;
	}
	return l;
}
int main()
{
	//freopen("in.txt","r",stdin);
	int i,j,k,ans=INF;
	scanf("%d%d",&n,&l);
	S=1<<n;
	for(i=0;i<n;i++)
	{
		scanf("%d%d",&len[i],&p[i]);
		for(j=0;j<p[i];j++)scanf("%d",&movie[i][j]);
	}
	memset(f,-1,sizeof(f));
	f[0]=0;
	for(i=0;i<S;i++)
	{
		if(f[i]==-1)continue;
		if(f[i]>=l)
		{
			for(j=0,k=i;k;k-=k&(-k))j++;
			ans=min(ans,j);
			continue;
		}
		for(j=0;j<n;j++)
		{
			if(i&(1<<j))continue;
			k=find(f[i],j);
			if(k==-1)continue;
			f[i|(1<<j)]=max(f[i|1<<j],movie[j][k]+len[j]);
		}
	}
	if(ans==INF)puts("-1");
	else printf("%d\n",ans);
	return 0;
}

总结

比赛时看数据规模感觉是状压DP来着……但是写不来
比赛时看到n<=20的数据规模就感觉是状压DP,但是却不知道怎么写最后放弃了,暴力也不成功。
反映出代码能力不足和对于状压DP的生疏


赛后总结

打OI这么久第一次参加的现场比赛,看到这些题毫无心理准备,感觉和自己以前写的题是两个概念。尤其是听到其他大佬敲得飞快时就更慌了。成绩出来我这个速成选手直接被打回原形,感觉之前的一切YY都破灭了,也对自己的菜有了充分的认识。恶补算法1个月上了14节课,就想挑翻那些高手,也太天真了,好歹别人系统训练一年了。但都到这份上了,还有啥好说的呢,只要还没出局,我会一直进攻。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值