200805-NOIP基础测试

今天的题lyf大神AK了!!!而我还是两百来分… …

T1

一个简单的dfs搜索加剪枝
(我唯一一道A了的题)

#include<bits/stdc++.h>
using namespace std;

int read()
{
	int i=0;char ch;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
	return i;
}

int n,m,k,ans=0x3f3f3f,a[20][20],s[20],sum[20][20],v[20];

void dfs(int x,int val)
{
	int tag=0;
	for(int i=1;i<=m;i++)
	{
		if(s[i]+sum[n][i]-sum[x-1][i]<k) return;
		if(s[i]<k) {tag=1;break;}
	}
	if(!tag) ans=min(ans,val);
	else
	{
		dfs(x+1,val);
		for(int i=1;i<=m;i++) s[i]+=a[x][i];
		dfs(x+1,val+v[x]);
		for(int i=1;i<=m;i++) s[i]-=a[x][i];
	}
}

int main()
{
	freopen("algor.in","r",stdin);
	freopen("algor.out","w",stdout);
	n=read();m=read();k=read();
	for(int i=1;i<=n;i++)
	{
		v[i]=read();
		for(int j=1;j<=m;j++) a[i][j]=read(),sum[i][j]=sum[i-1][j]+a[i][j];
	}
	for(int i=1;i<=m;i++) if(sum[n][i]<k) {printf("-1");return 0;}
	dfs(1,0);
	printf("%d",ans);
	return 0;
}

T2

一道思维题。
直接分解每个数的质因子暴力求解会T到飞起,只能过一个点。(我就是这么做的,只有10pts)
我们可以这样考虑:
对于一个数x,它的倍数一定含有x因子;
如果 ( m + 1 ) ∗ x > n (m+1)*x>n (m+1)x>n m x ≤ n mx\le n mxn ,则 x 这个因子一定会加 m 次。
所以我们只需要枚举n中的每个数 i i i ,将它在 [ 1 , n ] [1,n] [1,n]内的倍数全部加起来得到 S i S_i Si,再将 S i S_i Si求和得到的就是答案。

#include<bits/stdc++.h>
using namespace std;

int n;long long res;

int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j+=i) res+=j;//j循环可以用等差数列求和O(1)处理
	printf("%lld",res);
	return 0;
}

(std代码贼短,而我的巨长还T了… …)

T3

一道贪心题
考场上我是按物品的价值从大到小排序,依次枚举每个物品,若该时间没有放过物品,则放下,若已经有东西了,就向前移动,直到走到一个空的时间,若前面没有空的时间了,就放弃这个物品。
想法是正确的,但时间复杂度不允许( n ≤ 1 e 5 n\le1e5 n1e5),我想优化,结果打挂了… …
所以我们就需要一个 O ( n l o g n ) O(nlogn) O(nlogn)的优秀贪心。
我们可以按时间排序从小到大,不断枚举,若枚举个数超过当前最大时间,则踢掉最小的那个数,要用小根堆维护。

#include<bits/stdc++.h>
using namespace std;

int read()
{
	int i=0;char ch;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
	return i;
}

const int N=1e5+5;
struct node{
	int t,v;
}a[N];
int n,ans,pos;
priority_queue<int,vector<int>,greater<int> >q;

bool cmp(const node &a,const node &b) {return a.t<b.t;}

int main()
{
	freopen("mouse.in","r",stdin);
	freopen("mouse.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) a[i].t=read();
	for(int i=1;i<=n;i++) a[i].v=read();
	sort(a+1,a+n+1,cmp);
	for(int i=1;i<=n;i++)
	{
		if(pos<a[i].t)
		{
			pos++;
			q.push(a[i].v);
			ans+=a[i].v;
			continue;
		}
		int u=q.top();
		if(u<a[i].v)
		{
			q.pop();
			ans=ans-u+a[i].v;
			q.push(a[i].v);
		}
	}
	printf("%d",ans);
	return 0;
}

T4

求树上最长递增子序列。
直接dfs一遍,对每个节点 n l o g n nlogn nlogn 求一次最长递增子序列,回溯记录答案。
考场上我不知道怎么想的,在树上遍历了n遍,又双叒T了… …

#include<bits/stdc++.h>
using namespace std;

int read()
{
	int i=0;char ch;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
	return i;
}

const int N=2e5+5;
int n,a[N],tot,ans,mx[N],nxt[4*N],to[4*N],first[N],b[N];

void add(int x,int y)
{
	nxt[++tot]=first[x];
	first[x]=tot;
	to[tot]=y;
}

void dfs(int u,int fa)
{
	int an,flag,j;
	for(int e=first[u];e;e=nxt[e])
	{
		int v=to[e];
		if(v==fa) continue;
		an=ans;flag=0;
		if(b[ans]<a[v]) b[++ans]=a[v];
		else
		{
			j=lower_bound(b+1,b+ans+1,a[v])-b;
			flag=b[j];
			b[j]=a[v];
		}
		mx[v]=ans;
		dfs(v,u);
		ans=an;
		if(flag) b[j]=flag;
	}
}

int main()
{
	freopen("treelis.in","r",stdin);
	freopen("treelis.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) a[i]=read();
	for(int i=1;i<n;i++)
	{
		int x=read(),y=read();
		add(x,y);add(y,x);
	}
	ans=1;b[1]=a[1];mx[1]=1;
	dfs(1,0);
	for(int i=1;i<=n;i++) printf("%d\n",mx[i]);
	return 0;
}

T5

考场上我做的80分做法,暴力枚举两个点,判断是否有边,最后统计联通块个数,不知道为什么还WA了一个点… …
因为 A ≤ B ≤ 1 e 5 A\le B\le1e5 AB1e5,所以每个数最多拥有6个质因数(235711*13=30030),因此我们可以线性筛出 [ 1 , B ] [1,B] [1,B]内的所有质数,然后枚举它们在 [ A , B ] [A,B] [A,B]之间的倍数,将这些数放进一个联通块,最后统计联通块个数即可。

#include<bits/stdc++.h>
using namespace std;

int read()
{
	int i=0;char ch;
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) {i=i*10+ch-'0';ch=getchar();}
	return i;
}

const int N=1e5+5;
int fa[N],cnt,c[N],P,n,m,h[N],ans,tag[N];

int getfa(int x) {return x==fa[x] ? x : fa[x]=getfa(fa[x]);}

int main()
{
	freopen("set.in","r",stdin);
	n=read();m=read();P=read();
	for(int i=n;i<=m;i++) fa[i]=i;
	for(int i=2;i<=m;i++)
	{
		if(tag[i]) continue;
		c[++cnt]=i;
		for(int j=i;j<=m;j+=i)
			tag[j]=1;
	}
	for(int i=cnt;i>=1;i--)
	{
		if(c[i]<P) break;
		int t=n/c[i]*c[i];
		while(t<n) t+=c[i];
		int o=t;
		while(t<=m)
		{
			fa[getfa(t)]=getfa(o);
			t+=c[i];
		}
	}
	for(int i=n;i<=m;i++)
	{
		int t=getfa(i);
		if(!h[t]) {h[t]=1;ans++;}
	}
		
	printf("%d",ans);
	return 0;
}

总结

这次考试码量都不大,但考得也不是很好,T2问题没有转换成功,T3思维没有打开,被之前考试的一道题局限,T4想复杂了,T5没时间思考,下次得有所改进。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值