【考试总结】NOIP模拟 test10-27
Tips:
中文题目名称 |
选举 |
异象石 |
序列变换 |
英文题目与文件名 |
election |
stone |
change |
输入文件名 |
election.in |
stone.in |
change.in |
输出文件名 |
election.out |
stone.out |
change.out |
每个测试点时限 |
1秒 |
1秒 |
1秒 |
内存限制 |
256 MB |
256 MB |
256 MB |
测试点数目 |
10 |
10 |
10 |
每个测试点分值 |
10 |
10 |
10 |
结果比较方式 |
全文比较(过滤行末空格及文末回车) |
全文比较(过滤行末空格及文末回车) |
全文比较(过滤行末空格及文末回车) |
题目类型 |
传统 |
传统 |
传统 |
Problem
election(选举)
Description
C国的总统选举委员会最近遇到了一些麻烦。
他们在统计各省对H先生的支持率(百分比)时,把支持率四舍五入到了整数。等他们公布结果后,该国媒体发现这些省份的支持率之和不等于100(百分比)!在媒体黑幕声的质疑下,他们不得不找你寻求帮助。 你将得到各省四舍五入后的支持率,请计算这些省份的支持率在四舍五入前的和是否可能等于100?支持率是以百分比的形式统计的。
请注意,各省的支持率可以是一个包含任意多位的有限小数。一个小数在四舍五入到整数时,若小数点后第一位小于5则舍,大于等于5则入。
例如: 26、17、58是一种可能的支持率,因为它们可能是25.8、16.5、57.7四舍五入后得到的,而25.8+16.5+57.7=100。
49、49是一种不可能的支持率,因为当9的个数有限时,无论有多少个9,均有49.499…99+49.499…99<100。
Input Format
输入包含多组数据,第一行是一个整数T,表示数据组数。
接下来是T组数据,每组数据的第一行是一个整数N,表示参与选举的省份个数。第二行是N个整数,表示各省四舍五入后的支持率。
Output Format
对于每组数据,若是一种可能的支持率,输出Yes,否则输出No
Sample Input
2 2 49 49 3 26 17 58
Sample Output
No Yes
Hint
数据范围与约定
对于30%的数据,1<=n<=3;
对于50%的数据,1<=n<=5;
对于80%的数据,1<=四舍五入后各省的支持率<=99;
对于100%的数据,1<=n<=10000,输入数据中的所有整数均在有符号16位整数范围内。
Solution
一个数X在取舍前可能取[X-0,5,X+0.5)之间的任意数值。
需要注意0和100等边界问题,因为支持率(百分比)一定是一个[0,100]之间的数。
#include<cstdio> int T,n,flag,l=100,r=100; double sum; int main() { scanf("%d",&T); while (T--) { flag=1; sum=0; scanf("%d",&n); for (int i=1;i<=n;i++) { int x; scanf("%d",&x); if (0>x||x>100) flag=0; if (x<100) r+=0.5; if (x>0) l-=0.5; sum+=x; } if (l>=sum||sum>r) flag=0; if (flag) printf("Yes\n"); else printf("No\n"); } }
stone(异象石)
Description
Adera是Microsoft应用商店中的一款解谜游戏。
异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。这张地图上有N个点,有N-1条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:
1.地图的某个点上出现了异象石(已经出现的不会再次出现);
2.地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
3.向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。
Input Format
第一行有一个整数N,表示点的个数。
接下来N-1行每行三个整数x,y,z,表示点x和y之间有一条长度为z的双向边。
第N+1行有一个正整数M。
接下来M行每行是一个事件,事件是以下三种格式之一:
-
x 表示点x上出现了异象石
-
x表示点x上的异象石被摧毁
?表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
Output Format
对于每个 ?事件,输出一个整数表示答案。
Sample Input
6 1 2 1 1 3 5 4 1 7 4 5 3 6 4 2 10 + 3 + 1 ? + 6 ? + 5 ? - 6 - 3 ?
Sample Output
5 14 17 10
Hint
数据范围与约定
对于30%的数据,1 ≤ n, m ≤ 1000。
对于另20%的数据,地图是一条链,或者一朵菊花。
对于100%的数据,1 ≤ n, m ≤ 10^5, 1 ≤ x, y ≤ n, x ≠ y, 1 ≤ z ≤ 10^9。
Solution
因为每加入一个点,它一定会与它相邻的最近的点连边,
可以发现最终ans=dfs序相邻的点到lca的距离的和/2
用倍增求LCA,set维护dfs序
#include<set> #include<cstdio> #include<algorithm> std::set<int> s; int n,m,cnt; int num,head[100005]; long long ans,dis[100005],g[100005]; int f[100005][20],dep[100005],dfn[100005],seq[100005]; struct edge { int to,next,dis; }e[200005]; void add(int x,int y,int z) { e[++num].next=head[x]; e[num].to=y; e[num].dis=z; head[x]=num; } void dfs(int x,int fa) { f[x][0]=fa; dep[x]=dep[fa]+1; dfn[x]=++cnt; seq[cnt]=x; for (int i=head[x];i;i=e[i].next) { int v=e[i].to; if (v!=fa) { dis[v]=dis[x]+e[i].dis; dfs(v,x); } } } void get_fa() { for (int j=1; j<=19; j++) for (int i=1; i<=n; i++) f[i][j]=f[f[i][j-1]][j-1]; } int LCA(int x,int y) { if (dep[x]<dep[y]) std::swap(x,y); int t=dep[x]-dep[y]; for (int i=0;i<=16;i++) if (t&(1<<i)) x=f[x][i]; if (x==y) return x; for (int i=16;i>=0;i--) { if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } long long dist(int x,int y) { return dis[x]+dis[y]-dis[LCA(x,y)]*2; } void find(int x,int &l,int &r) { std::set<int>::iterator it=s.lower_bound(x); if (it!=s.end()) r=*it;else r=*s.begin(); if (it==s.begin()) it=s.end(); l=*(--it); l=seq[l],r=seq[r]; } void insert(int x) { if (!s.empty()) { int l,r; find(dfn[x],l,r); ans-=g[r]; g[x]=dist(l,x); g[r]=dist(x,r); ans+=g[x]+g[r]; } else g[x]=0; s.insert(dfn[x]); } void erase(int x) { int l,r; s.erase(dfn[x]); if (!s.empty()) { find(dfn[x],l,r); ans-=g[x]+g[r]; g[r]=dist(l,r); } ans+=g[r]; } int main() { scanf("%d",&n); for (int i=1;i<n;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z),add(y,x,z); } dfs(1,0); get_fa(); scanf("%d",&m); for (int i=1;i<=m;i++) { char ch[10]; scanf("%s",ch); if (ch[0]=='?') printf("%lld\n",ans/2); else { int x; scanf("%d",&x); if (ch[0]=='+') insert(x); else erase(x); } } }
change(序列变换)
Description
给定一个长度为N的数列Ai。
你可以对数列进行若干次操作,每次操作可以从数列中任选一个数,把它移动到数列的开头或者结尾。
求最少经过多少次操作,可以把数列变成单调不减的。“单调不减”意味着数列中的任意一个数都不大于排在它后边的数。
Input Format
第一行是一个正整数N。
第二行是N个正整数Ai。
Output Format
输出一个整数,表示最少需要的操作次数。
Sample Input
5 6 3 7 8 6
Sample Output
2
Hint
数据范围与约定
对于30%的数据,满足1≤n≤10。
对于60% 的数据,满足1≤n≤1000。
对于100% 的数据,满足1≤n≤1000000,1≤Ai≤1000000。
Solution
要求最小的移动次数,必然要保持一个值连续、编号单调递增的子序列不动,
则ans=n-子序列长度,求这个子序列即可。
#include<cstdio> #include<algorithm> int n,ans; int a[1000005],b[1000005]; bool cmp(int x,int y) { if (a[x]==a[y]) return x<y; return a[x]<a[y]; } int main() { scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=i; std::sort(b+1,b+n+1,cmp); for (int i=1,j;i<=n;i=j+1) { for (j=i;j<n&&b[j]<b[j+1];j++); int len=j-i+1; for (int k=i-1;k&&a[b[k]]==a[b[i-1]];k--) if (b[k]<b[i]) len++; for (int k=j+1;k<=n&&a[b[j+1]]==a[b[k]];k++) if (b[j]<b[k]) len++; ans=std::max(ans,len); } printf("%d",n-ans); }