NOIP2016提高组模拟赛
——By wangyurzee7
中文题目名称 | 迷妹 | 膜拜 | 换数游戏 |
英文题目与子目录名 | fans | mod | game |
可执行文件名 | fans | mod | game |
输入文件名 | fans.in | mod.in | game.in |
输出文件名 | fans.out | mod.out | game.out |
每个测试点时限 | 1秒 | 1秒 | 1秒 |
测试点数目 | 20 | 16 | 16 |
每个测试点分值 | 10 | 6.25 | 6.25 |
题目类型 | 传统 | 传统 | 传统 |
运行内存上限 | 128M | 128M | 256M |
注意:评测时不打开任何优化开关
迷妹
(fans.cpp/c/pas)
【问题描述】
小钟、小皓和小曦都是著名偶像派OI选手,他们都有很多迷妹。
现在,有n个妹子排成了一行,从左到右编号为1到n。这些妹子中,任意一个都是其中一个人的迷妹。
现在,蒟蒻wyz有Q个问题,第i个问题为:编号在l[i]到r[i]范围内的妹子中,分别有几个小钟的迷妹、小皓的迷妹、和小曦的迷妹。
【输入格式】
输入到fans.in
第一行2个正整数n,Q。
第2行到第n+1行每行一个正整数a[i],描述了第i个妹子是谁的迷妹。a[i]=1表示小钟的迷妹,a[i]=2表示小皓的迷妹,a[i]=3表示小曦的迷妹。
第n+2行到第n+Q+1行,每行2个整数,表示第i个问题。
【输出格式】
输出到fans.out
共Q行,每行3个用空格分开的整数,分别表示对于第i个问题,有多少小钟、小皓、小曦的迷妹。
【输入输出样例】
fans.in | fans.out |
6 3 2 1 1 3 2 1 1 6 3 3 2 4 | 3 2 1 1 0 0 2 0 1 |
【数据范围】
对于10%的数据,保证1<=n<=10,Q<=10,
对于25%的数据,保证1<=n<=100,Q<=100,
对于45%的数据,保证1<=n<=1000,Q<=1000,
对于100%的数据,保证1<=n<=100,000,Q<=100,000。
保证1<=a[i]<=3,1<=l[i]<=r[i]<=n。
//前缀和#include#include #include #define maxn 1000002using namespace std;int n,q,cnt;int a1[maxn],a2[maxn],a3[maxn],a[maxn];int s1[maxn],s2[maxn],s3[maxn];inline int init(){ int x=0,f=1;char c=getchar(); while(c>'9'||c<'0'){ if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();} return x*f;}int main(){ freopen("fans.in","r",stdin); freopen("fans.out","w",stdout); n=init();q=init(); for(int i=1;i<=n;i++) { a[i]=init(); if(a[i]==1) a1[i]=1; if(a[i]==2) a2[i]=1; if(a[i]==3) a3[i]=1; s1[i]=s1[i-1]+a1[i]; s2[i]=s2[i-1]+a2[i]; s3[i]=s3[i-1]+a3[i]; } int l,r; for(int i=1;i<=q;i++) { l=init();r=init(); printf("%d %d %d ",s1[r]-s1[l-1],s2[r]-s2[l-1],s3[r]-s3[l-1]); printf("\n"); } fclose(stdin); fclose(stdout); return 0;}
膜拜
(mod.cpp/c/pas)
【问题描述】
有一个n个点m条边的有向图,wyz可以从1号点出发在图上走,并且最终需要回到1号点。每个点都有一个神犇(包括1号点),每次经过一个没到过的点,wyz都会膜拜那位神犇。wyz希望膜拜尽可能多的神犇。
由于wyz膜拜神犇的欲望非常强烈,所以他可以有一次机会逆着一条有向边的方向走。(需要注意的是,这条边的方向不会改变)
你现在想知道,wyz最多能膜拜多少神犇?
【输入格式】
输入到mod.in
第一行2个整数n、m,分别表示图的点数和边数。
第2行到底m+1行,每行两个整数u,v,描述一条u到v的有向边。
【输出格式】
输出到mod.out
一行一个整数表示wyz最多能膜拜多少神犇。
【输入输出样例】
mod.in | mod.out |
7 10 1 2 3 1 2 5 2 4 3 7 3 5 3 6 6 5 7 2 4 7 | 6 |
【数据范围】
对于25%的数据,保证n<=100,m<=250,
对于43.75%的数据,保证n<=3,000,m<=7,000。
对于100%的数据,保证n,m<=100,000。
/*可以先进行缩点 ,缩点后的点权为点所代表的原强连通分量中点的个数,这样我们就把它变成了更好处理的DAG。处理后的所有点总共能分成三类:1.能到达点1的2.从点1出发能到达的3.和点1没有任何关系的很显然可以删除第三类点,因为就算怎么翻转路径,第三类的点都和答案没有任何关系。而最终要反转的边在一开始一定是一条从第二类点到第一类点的边,我们把这样的边全部反转,把能达到点1的路径统统改成到达点n+1,这样问题就简化成了在DAG中找一条从点1到点n+1的点权和最大的路径,可以用拓扑+dp解决了;*/#include#include #include #include #define maxn 200000using namespace std;struct point { int to,next;} edge[maxn],edge1[maxn][3];int cnt,n,m,ans,t,tot;int head[maxn],head1[maxn][3],d[maxn][3];int dfn[maxn],low[maxn],v[maxn],q[maxn];int belong[maxn],f[maxn][3],size[maxn];stack s;void add(int u,int v) { edge[++cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}void add1(int u,int v,int ch){ if (ch==1) cnt++; d[v][ch]++; edge1[cnt][ch].to=v; edge1[cnt][ch].next=head1[u][ch]; head1[u][ch]=cnt;}void tarjan(int u) { dfn[u]=low[u]=++t; s.push(u); for(int i=head[u]; i; i=edge[i].next) if(dfn[edge[i].to]==0) { tarjan(edge[i].to); low[u]=min(low[u],low[edge[i].to]); } else if (!v[edge[i].to]) low[u]=min(low[u],dfn[edge[i].to]); if (dfn[u]==low[u]) { tot++; int now=0; while (now!=u) { now=s.top(); s.pop(); v[now]=true; belong[now]=tot; size[tot]++; } }}void find(int ch) { f[belong[1]][ch]=size[belong[1]]; int ta=0; for (int i=1; i<=tot; i++) if (d[i][ch]==0) q[++ta]=i; while(ta>0) { int p=q[ta--]; for(int i=head1[p][ch];i;i=edge1[i][ch].next) { f[edge1[i][ch].to][ch]=max(f[edge1[i][ch].to][ch],f[p][ch]+size[edge1[i][ch].to]); if(--d[edge1[i][ch].to][ch]==0) q[++ta]=edge1[i][ch].to; } }}int main() { freopen("mod.in","r",stdin); freopen("mod.out","w",stdout); int x,y; scanf("%d%d",&n,&m); for (int i=1; i<=m; i++) { scanf("%d%d",&x,&y); add(x,y); } t=0; for(int i=1;i<=n; i++) if (!v[i]) tarjan(i); cnt=0; for(int i=1;i<=n;i++) for(int j=head[i]; j; j=edge[j].next) if(belong[i]!=belong[edge[j].to]) { add1(belong[i],belong[edge[j].to],1); add1(belong[edge[j].to],belong[i],2); } memset(f,0xef,sizeof f); find(1); find(2); ans=size[belong[1]]; for(int i=1; i<=n; i++) for(int j=head[i]; j; j=edge[j].next) if(belong[i]!=belong[edge[j].to]) ans=max(ans,f[belong[edge[j].to]][1]+f[belong[i]][2]-size[belong[1]]); printf("%d\n",ans); fclose(stdin); fclose(stdout); return 0;}
换数游戏
(game.cpp/c/pas)
【问题描述】
咸鱼wyz最近沉迷于一款游戏,叫做换数游戏。
游戏开始时,有n个1到40之间的整数排成一排。wyz每次可以将两个相邻的相等的正整数替换成一个比原数大一的正整数(需要注意的是,合并后的数可以大于40)。
wyz可以在任意时刻结束游戏。结束时wyz的得分为所有剩余的数中最大的数。
现在给定一个初始局面,你想知道wyz能获得的最高得分。
【输入格式】
输入到game.in
第一行一个正整数n。
第2行到第n+1行每行一个正整数,描述第i个数a[i]。
【输出格式】
输出到game.out
一行一个整数表示wyz能够获得的最高得分。
【输入输出样例】
game.in | game.out |
4 1 1 1 2 | 3 |
【数据范围】
对于6.25%的数据,保证n<=5。
对于12.5%的数据,保证n<=20。
对于43.75%的数据,保证n<=1024。
对于100%的数据,保证n<=262144。
数据保证1<=a[i]<=40。
#include#include #include #define maxn 666666using namespace std;int n,num,cnt,now,NOW;int a[maxn],v[maxn];void find(int x){ for(int i=1;i<=n;i++) { now=0;NOW=0;cnt=0; if(a[i]==x&&a[i+1]==x) { now=i,NOW=i; while(a[now]==x) now++;now--; int d=now-NOW+1; if((d%2&&a[NOW-1] a[now+1]&&now!=n)||(d%2&&NOW==1&&now!=n))NOW+=1; d/=2; for(int j=NOW+1;j<=n;j++) a[j]=a[j+d];n-=d; while(cnt
正解:
/*: nxt[i]表示从第i个数开始能够凑出k来的下一个位置每一步都依赖于前一步的状态 nxt[nxt[i]]=i+1 意思是如果以当前i能凑出k-1来,并且以下一位开头也能凑出k-1来,就把他的下一位置调到第二个能凑出k-1之后,然后更新答案=k。*/#include#define N 300005using namespace std;inline int read(){ int ret=0;char ch=getchar(); while (ch<'0'||ch>'9') ch=getchar(); while ('0'<=ch&&ch<='9'){ ret=ret*10-48+ch; ch=getchar(); } return ret;}int a[N],n;int nxt[N];int main(){// freopen("game.in","r",stdin);// freopen("game.out","w",stdout); n=read(); for (int i=1;i<=n;++i) a[i]=read(); memset(nxt,0,sizeof(nxt)); int ans=0; for (int k=1;k<=60;++k) for (int i=1;i<=n;++i) if (a[i]==k) nxt[i]=i+1,ans=k; else if (nxt[i]&&nxt[nxt[i]]) nxt[i]=nxt[nxt[i]],ans=k; else nxt[i]=0; printf("%d\n",ans); return 0;}