20190525NOIP模拟赛

写在前面

后来补上的blog

题解报告: By 林希
题解引用林希的题解报告,会以图片形式给出

在这里插入图片描述
在这里插入图片描述

Problem 1 食物中毒(medicine.pas/c/cpp)

在这里插入图片描述在这里插入图片描述在这里插入图片描述

分析

emm,直接暴力有80分。
看看那个异或操作,想一想位运算优化

在这里插入图片描述

代码

80分(不加位运算优化)

/***************************
User:Mandy.H.Y
Language:c++
Problem:medicine
Algorithm:DFS
Scors:80
***************************/
#include<bits/stdc++.h>

using namespace std;

const int maxn=25;
const int maxm=55;

int n,m;
bool ans[maxm],vis[maxn];
int med[maxn][maxm],cnt[maxm],ned[maxm],tot[maxm],a[maxn];
bool S[(1<<21)+5];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("medicine10.in","r",stdin);
	freopen("medicine.out","w",stdout);
}

bool dfs(int dep,int u){
	int judge=1;

	for(int i=1;i<=m;++i){
		if(tot[ned[i]]%2==0){
			judge=0;
			break;
		}
	}

	if(judge) return 1;
	if(dep>n) return 0;	
	
	for(int i=u+1;i<=n;++i){
		
		if(vis[i]) continue;
		vis[i]=1;//其实可以不要 vis 
		
		for(int j=1;j<=med[i][0];++j){
			
			++tot[med[i][j]];
			
			if(ans[med[i][j]]&&tot[med[i][j]]==cnt[med[i][j]]&&(tot[med[i][j]]%2==0)) {
		
				for(int k=1;k<=j;++k){
					--tot[med[i][k]];//可以换成一伙 
				}
				vis[i]=0;//要先撤回标记再return 
		//这件事提醒我们要边写边加注 
				return 0;
			}
		}
		
		if(dfs(dep+1,i)) return 1;
		
		vis[i]=0;
		for(int j=1;j<=med[i][0];++j){
			--tot[med[i][j]];
		}
	
	}
	return 0;
}

void init(){
	memset(ans,0,sizeof(ans));
	memset(tot,0,sizeof(tot));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
}

void readdata(){
	for(int i=1;i<=m;++i){
		int x;read(x);ned[i]=x;
		ans[x]=1;
	}
	
	for(int i=1;i<=n;++i){
		read(med[i][0]);
		a[i]=i;
		for(int j=1;j<=med[i][0];++j){
			read(med[i][j]);
			++cnt[med[i][j]];
		}
	}
}

void work(){
	while(~scanf("%d%d",&n,&m)){
		int judge=0;
	
		init();
		readdata();
	
		for(int i=1;i<=m;++i){
			if(cnt[ned[i]]==0) {
				judge=1;
				break;
			}
		}
	
		if(judge){
			printf("Impossible\n");
			continue;
		}
		
		if(dfs(1,0)) printf("Possible\n");
		else printf("Impossible\n");
	}
}

int main(){
//	int times=clock();
	file();
	work();
//	printf("%.4llf",double(clock()-times)/CLOCKS_PER_SEC);
	return 0;
}

100分

/***************************
User:Mandy.H.Y
Language:c++
Problem:medicine
Algorithm:DFS+位运算优化 
Scors:100
***************************/
#include<bits/stdc++.h>

using namespace std;

const int maxn=25;
const int maxm=55;

int n,m;
long long S;
long long med[maxn];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("medicine10.in","r",stdin);
	freopen("medicine.out","w",stdout);
}

bool dfs(int dep,int u,long long s){
	
	if((S&s)==S) return 1;

	if(dep>n) return 0;	
	
	for(int i=u+1;i<=n;++i){
		long long now=s;
		now^=med[i];
		
		if(dfs(dep+1,i,now)) return 1;
	}
	return 0;
}

void init(){
	memset(med,0,sizeof(med));
}

void readdata(){
	for(int i=1;i<=m;++i){
		long long x;read(x);
		S|=((long long)1<<x);
	}
	
	for(int i=1;i<=n;++i){
		int x;
		read(x);
		for(int j=1;j<=x;++j){
			long long y;
			read(y);
			med[i]^=((long long)1<<y);
		}
	}
}

void work(){
	while(~scanf("%d%d",&n,&m)){
		S=0;
		
		init();
		readdata();
		
		if(dfs(1,0,0)) printf("Possible\n");
		else printf("Impossible\n");
	}
}

int main(){
//	int times=clock();
//	file();
	work();
//	printf("%.4llf",double(clock()-times)/CLOCKS_PER_SEC);
	return 0;
}

Problem 2 消息传递(message.pas/c/cpp)

在这里插入图片描述在这里插入图片描述

分析

最简单的一道,相当于tarjan的模板了
在这里插入图片描述

代码

/***************************
User:Mandy.H.Y
Language:c++
Problem:message
Algorithm:tarjan
***************************/

#include<bits/stdc++.h>

using namespace std;

const int maxn=1e5+5;
const int maxm=2e5+5;

int n,m,first[maxn],size=0,cnt=0,tp=0;
int dfn[maxn],low[maxn];
bool vis[maxn],ans[maxn];
int st[maxn];

struct Edge{
	int v,nt;
}edge[maxm];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("message.in","r",stdin);
	freopen("message.out","w",stdout);
}

void eadd(int u,int v){
	edge[++size].v=v;
	edge[size].nt=first[u];
	first[u]=size;
}

void readdata(){
	read(n);read(m);
	for(int i=1;i<=m;++i){
		int u,v;read(u);read(v);
		eadd(u,v);
	}
}

void tarjan(int u){
	dfn[u]=low[u]=++cnt;
	st[tp++]=u;
	vis[u]=1;
	for(int i=first[u];i;i=edge[i].nt){
		int v=edge[i].v;
		
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(vis[v]) low[u]=min(low[u],dfn[v]);
	}
	
	if(dfn[u]==low[u]){
		int now,judge=0;
		if(st[tp-1]!=u) judge=1;
		do{
			now=st[tp-1];
			--tp;
			vis[now]=0;
			if(judge) ans[now]=1;
		}while(now!=u);
	}
}

void work(){
//	int times=clock();
	for(int i=1;i<=n;++i)
		if(!dfn[i]) tarjan(i);
	
	for(int i=1;i<=n;++i){
		if(ans[i]) printf("T\n");
		else printf("F\n");
	}
//	printf("%.4llf",double(clock()-times)/CLOCKS_PER_SEC);
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

Problem 3 周年纪念日(anniversary.pas/c/cpp)

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

分析

最小瓶颈路 + 树形DP
在这里插入图片描述在这里插入图片描述

代码

prim:

/*****************************
User:Mandy.H.Y
Language:c++
Problem:
Algorithm:Prim
*****************************/

//日常证明:虽然是一个很简单的结论,很容易就想到了 

//求最小花费:最小生成树

//看最小生成树原理:
//prim贪点,类似Dijkstra,松弛原理
//kruskal贪边,先排序,再用并查集合并

//最小生成树保证了最大边最小

//证明: 
//如果有一种方案满足最大边小于最小生成树,
//且总边权大于最小生成树,记这颗生成树为 A 
//我们从最小生成树得到这颗生成树
//先断边:把最小生成树中大于A的最大边的边断掉
//这一步必会把 A 中最大边所连的两点断开
//如果没有被断掉,说明最大值不是这一个,因为两点间有更小的最大值
//再连边,连上最大边,把剩下的点用小于最大边的边连上
//注意:因为刚刚断掉的边都是大于最大边的, 现在所连的边都小于等于最大边,边权之和更小
//而刚刚的被断的是最小生成树,矛盾
//所以 最小生成树保证了最大边最小

//BY Mandy.H.Y 20190525 

//PS.对于第2个问,有没有很像 伟大的奶牛聚集Great cow gathering?
//详见luogu2986 

#include<bits/stdc++.h>

using namespace std;

const int maxn=1e5+5;
const int maxm=2e5+5;

typedef long long LL;
typedef pair<long long,long long> pir;

priority_queue<pir,vector<pir>,greater<pir> >q;

LL n,m,size,totcost,maxw,size1,totp,id,happy;
LL first1[maxn],first[maxn],p[maxn],dis[maxn],father[maxn];
LL cnt[maxn],f[maxn];
bool vis[maxn];

struct Edge{
	LL v,nt,w;
}edge[maxm<<1],edge1[maxn<<1];

template<class T>inline void read(T &x){
	x=0;bool flag=0;char ch=getchar();
	while(!isdigit(ch)) flag|=ch=='-',ch=getchar();
	while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
	if(flag)x=-x;
}

template<class T>void putch(const T x){
	if(x>9) putch(x/10);
	putchar(x%10|48);
}

template<class T>void put(const T x){
	if(x<0) putchar('-'),putch(-x);
	else putch(x);
}

void file(){
	freopen("1.txt","r",stdin);
}

void eadd(LL u,LL v,LL w){
	edge[++size].v=v;
	edge[size].w=w;
	edge[size].nt=first[u];
	first[u]=size;
}

void eadd1(LL u,LL v,LL w){
	edge1[++size1].v=v;
	edge1[size1].w=w;
	edge1[size1].nt=first1[u];
	first1[u]=size1;
}

void readdata(){
	read(n);read(m);
	for(int i=1;i<=n;++i) read(p[i]),totp+=p[i];
	for(int i=1;i<=m;++i){
		long long u,v,w;
		read(u);read(v);read(w);
		eadd(u,v,w);eadd(v,u,w);
	}
}

void prim(){
	for(int i=2;i<=n;++i) dis[i]=2e15;
	q.push(make_pair((LL)0,(LL)1));
	while(!q.empty()){
		LL u=q.top().second;q.pop();
		if(vis[u]) continue;
		totcost+=dis[u];
		eadd1(father[u],u,dis[u]);
		vis[u]=1;
		maxw=max(maxw,dis[u]);
		for(int i=first[u];i;i=edge[i].nt){
			LL v=edge[i].v,w=edge[i].w;
			if(w<dis[v]){
				dis[v]=w;
				father[v]=u;
				q.push(make_pair(dis[v],v));
			}
		}
	}
}

void dfs1(LL u){
	for(int i=first1[u];i;i=edge1[i].nt){
		LL v=edge1[i].v,w=edge1[i].w;
		dis[v]=dis[u]+w;
		dfs1(v);
		p[u]+=p[v];
		f[u]+=f[v]+p[v]*w;//先算出子树中的代价 
	}
}

void dfs2(LL u){
	for(int i=first1[u];i;i=edge1[i].nt){
		LL v=edge1[i].v,w=edge1[i].w;
		f[v]=f[u]-p[v]*w+(totp-p[v])*w;
//子节点为根的代价 =节点子树的代价 + (父节点为根的代价 - 子节点子树的代价 -  子节点的子树的居民从子节点转移到父节点的代价)+ 剩余节点从父节点 转移到子节点的代价
// == 父节点为根的代价  -  子节点的子树的居民从子节点转移到父节点的代价+ 剩余节点从父节点 转移到子节点的代价
 //()里面等于剩余节点到父节点的代价 
		if(f[v]<happy){
			happy=f[v];id=v;
		}
		dfs2(v);
	}
}

void work(){
	
	prim();
	put(totcost);putchar(' ');
	put(maxw);putchar('\n');
	dfs1(1);
	happy=f[1];id=1;
	dfs2(1);
	put(id);putchar(' ');
	put(happy);putchar('\n');
}

int main(){
//	file();
	readdata();
	work();
	return 0;
}

kruskal

#include <bits/stdc++.h>
using namespace std;
 
#define int long long
 
const int MAXN = 100005;
 
struct edge
{
    int to,nxt,wgt;
} info[MAXN << 1];
 
struct node
{
    int from,to,wgt;
    friend bool operator <(const node &na,const node &nb)
    {
        return na.wgt < nb.wgt;
    }
} Edge[MAXN << 1];
 
int n,m,e,ui,vi,wi,cost,maxd,minID = INT_MAX;
long long mind = LLONG_MAX;
int head[MAXN],ufs[MAXN],sumPopulation[MAXN],population[MAXN];
long long dist[MAXN];
 
template<typename T> void qread(T &sum)
{
    sum = 0;
    register char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9')
    {
        sum = (sum << 1) + (sum << 3) + ch - '0';
        ch = getchar();
    }
}
 
template<typename T> void qwrite(const T x)
{
    if (x < 0)
    {
        putchar('-');
        qwrite(-x);
    }
    else
    {
        if (x >= 10) qwrite(x / 10);
        putchar(x % 10 + '0');
    }
}
 
inline void addedge(int from,int to,int wgt)
{
    info[++e].to = to;
    info[e].wgt = wgt;
    info[e].nxt = head[from];
    head[from] = e;
}
 
inline int find(int u)
{
    return ufs[u] == u ? u : ufs[u] = find(ufs[u]);
}
 
void preDfs(int u,int f)
{
    for (int i = head[u]; i; i = info[i].nxt)
    {
        int v = info[i].to;
        if (v == f) continue;
        preDfs(v,u);
        sumPopulation[u] += sumPopulation[v];
        dist[u] += dist[v] + sumPopulation[v] * info[i].wgt;
    }
}
 
void postDfs(int u,int f)
{
    for (int i = head[u]; i; i = info[i].nxt)
    {
        int v = info[i].to;
        if (v == f) continue;
        dist[v] += dist[u] - sumPopulation[v] * info[i].wgt + (sumPopulation[1] - sumPopulation[v]) * info[i].wgt;
        postDfs(v,u);
    }
}
 
void init()
{
//  freopen("anniversaryIN.txt","r",stdin);
//  freopen("anniversaryOUT.txt","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (int i = 1; i <= n; ++i) ufs[i] = i;
    for (int i = 1; i <= n; ++i)
    {
        qread(population[i]);
        sumPopulation[i] = population[i];
    }
    for (int i = 1; i <= m; ++i)
    {
        qread(Edge[i].from);
        qread(Edge[i].to);
        qread(Edge[i].wgt);
    }
    sort(Edge + 1,Edge + m + 1);
}
 
void work()
{
    int used = 0;
    for (int i = 1; i <= m; ++i)
    {
        if (used == n - 1) break;
        int u = find(Edge[i].from);
        int v = find(Edge[i].to);
        if (u != v)
        {
            ufs[u] = v;
            cost += Edge[i].wgt;
            maxd = max(maxd,Edge[i].wgt);
            addedge(Edge[i].from,Edge[i].to,Edge[i].wgt);
            addedge(Edge[i].to,Edge[i].from,Edge[i].wgt);
            used++;
        }
    }
    preDfs(1,0);
    for (int i = 2;i <= n;++i) dist[i] = 0;
    postDfs(1,0);
    for (int i = 1; i <= n; ++i)
    {
        if (mind > dist[i])
        {
            minID = i;
            mind = dist[i];
        }
    }
    printf("%lld %lld\n%lld %lld",cost,maxd,minID,mind);
}
 
#undef int
 
int main()
{
    init();
    work();
    return 0;
}
NOI(全国青少年信息学奥林匹克竞模拟的测试数据是指用于评测参选手的程序的输入和对应的输出。测试数据是非常重要的,因为它决定了参选手的程序能否正确地解决问题。 在NOI模拟中,测试数据具有以下特点: 1.充分覆盖:测试数据应涵盖各种可能的输入情况,包括边界条件和极端情况。通过提供不同的测试数据,可以考察选手对问题的全面理解和解决能力。 2.随机性和均衡性:为了公平起见,测试数据应该是随机生成的,而不是针对某个特定算法或解法设计的。同时,测试数据应该是均衡的,即各种情况的概率应该大致相等,以避免偏向某些解法。 3.合理性和可行性:测试数据应该是合理和可行的,即符合题目要求的输入数据,并且是选手能够通过编写程序来处理的。测试数据应该考虑到程序的限制和时间复杂度,以充分测试选手的编程能力。 NOI模拟的测试数据通常由经验丰富的考题组负责生成。他们会根据题目的要求和限制,设计出一组合理、充分、随机和均衡的测试数据,以确保参选手的程序在各种情况下都能正确运行,并且能通过性能测试。 总之,测试数据在NOI模拟中起到了至关重要的作用,它既考察了选手对问题的理解和解决能力,又提高了选手编程的技巧和效率。同时,合理和恰当的测试数据也是公平竞的保证,确保每个参选手有相同的机会和条件进行竞争。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值