//来自泉七的试题 虽然T1出的比较玄学 好在后来都及时更正题目了
选举
(election.pas/c/cpp)
题目描述
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。
输入格式
输入包含多组数据,第一行是一个整数T,表示数据组数。
接下来是T组数据,每组数据的第一行是一个整数N,表示参与选举的省份个数。第二行是N个整数,表示各省四舍五入后的支持率。
输出格式
对于每组数据,若是一种可能的支持率,输出Yes,否则输出No。
样例输入
2
2
49 49
3
26 17 58
样例输出
No
Yes
数据范围与约定
对于30%的数据,1<=n<=3;
对于50%的数据,1<=n<=5;
对于80%的数据,1<=四舍五入后各省的支持率<=99;
对于100%的数据,1<=n<=10000,输入数据中的所有整数均在有符号16位整数范围内。
Solution:
可以求出使用给出的N个支持率,最终可能的支持率的取值范围,检查该范围是否包含100。
一般情况下,一个数X在取舍前可能取[X-0,5,X+0.5)之间的任意数值。
需要注意0和100等边界问题,因为支持率(百分比)一定是一个[0,100]之间的数。
求出的取值范围最好使用整数并加以开闭区间判断,避免实数精度误差。
Other:
正解当然是dp,但实际上数学方法也是可以过的。(%OMG_link巨大)
数据比较水,精度差只有一个点,所以写(*0.49999999999...)也是可以得到90分的。
#include<cstdio> #define MAXN 10005 #define debug 0 using namespace std; int T,n; int main(){ freopen("election.in","r",stdin); freopen("election.out","w",stdout); scanf("%d",&T); while(T--){ int big=0,sma=0,sum=0; bool flag=false; scanf("%d",&n); for(int i=1;i<=n;i++){ int a;scanf("%d",&a); if(a<0||a>100) flag=true; if(a==0) sma++; else if(a==100) big++; else sma++,big++; sum+=a; } if(flag) {puts("No");continue;} if(sum==100) puts("Yes"); if(sum>100){ if((sum-100)*2<=big) puts("Yes"); else puts("No"); } if(sum<100){ if((100-sum)*2<sma) puts("Yes"); else puts("No"); } } return 0; }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<string> 6 #include<vector> 7 #include<map> 8 #include<ctime> 9 using namespace std; 10 long long sum, l, r; 11 int T, n, x; 12 13 int main() 14 { 15 freopen("election.in","r",stdin); 16 freopen("election.out","w",stdout); 17 cin>>T; 18 while(T--) 19 { 20 cin >> n; 21 int cnt0 = 0, cnt100 = 0, flag = 0; 22 sum = 0; 23 for (int i = 1; i <= n; i++) 24 { 25 scanf("%d", &x); 26 sum += x; 27 if (x == 0) { 28 ++cnt0; 29 } 30 if (x == 100) { 31 ++cnt100; 32 } 33 if (x > 100) { 34 flag = 1; 35 } 36 } 37 if (flag) { puts("No"); continue; } 38 l = sum - (n - cnt0) / 2, r = sum + (n - cnt100) / 2; 39 bool cond0 = l <= 100 ; 40 bool cond1; 41 if((n - cnt100) & 1 || n - cnt100 == 0) cond1= (r>=100); else cond1= (r>100); 42 if (cond0 && cond1) { 43 puts("Yes"); 44 } 45 else { 46 puts("No"); 47 48 } 49 } 50 }
异象石
(stone.pas/c/cpp)
题目描述
Adera是Microsoft应用商店中的一款解谜游戏。
异象石是进入Adera中异时空的引导物,在Adera的异时空中有一张地图。这张地图上有N个点,有N-1条双向边把它们连通起来。起初地图上没有任何异象石,在接下来的M个时刻中,每个时刻会发生以下三种类型的事件之一:
- 地图的某个点上出现了异象石(已经出现的不会再次出现);
- 地图某个点上的异象石被摧毁(不会摧毁没有异象石的点);
- 向玩家询问使所有异象石所在的点连通的边集的总长度最小是多少。
请你作为玩家回答这些问题。
输入格式
第一行有一个整数N,表示点的个数。
接下来N-1行每行三个整数x,y,z,表示点x和y之间有一条长度为z的双向边。
第N+1行有一个正整数M。
接下来M行每行是一个事件,事件是以下三种格式之一:
+ x 表示点x上出现了异象石
- x表示点x上的异象石被摧毁
?表示询问使当前所有异象石所在的点连通所需的边集的总长度最小是多少。
输出格式
对于每个 ?事件,输出一个整数表示答案。
样例输入
6
1 2 1
1 3 5
4 1 7
4 5 3
6 4 2
10
+ 3
+ 1
?
+ 6
?
+ 5
?
- 6
- 3
?
样例输出
5
14
17
10
数据范围与约定
对于30%的数据,1 ≤ n, m ≤ 1000。
对于另20%的数据,地图是一条链,或者一朵菊花。
对于100%的数据,1 ≤ n, m ≤ 10^5, 1 ≤ x, y ≤ n, x ≠ y, 1 ≤ z ≤ 10^9。
Solution:
LCA+set。
如果在a1,a2...ak这些点上有异象石,则所需代价就是按照DFS序依次遍历这k个点再回到根的总距离。
这个距离数还等于a1,a2...ak排成一个环形序列,相邻两个点的距离之和。
因此用set维护有异象石的点构成的dfs序,用一个全局变量记录答案,插入、删除时更新set的同时利用LCA算法更新答案。
Other:
并不会,留坑咯~
据说这道题是某爆炸OJ原题,直接拿来当考题不走心呀。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<set> 6 #include<queue> 7 #include<cmath> 8 using namespace std; 9 const int u=100010; 10 int f[u][20],a[u],d[u],v[u],ver[2*u],next[2*u],edge[2*u],head[u]; 11 long long dis[u][20],ans; 12 int n,m,tot,t,i,j,x,y,z; 13 set<int> s; 14 typedef set<int>::iterator It; 15 It it; 16 queue<int> q; 17 char str[2]; 18 19 void add(int x,int y,int z) 20 { 21 ver[++tot]=y,edge[tot]=z,next[tot]=head[x],head[x]=tot; 22 } 23 24 void bfs() 25 { 26 q.push(1),v[1]=1,d[1]=1; 27 while(q.size()) 28 { 29 x=q.front(); q.pop(); 30 for(i=head[x];i;i=next[i]) 31 if(!v[y=ver[i]]) 32 { 33 q.push(y); v[y]=1; 34 d[y]=d[x]+1; 35 f[y][0]=x,dis[y][0]=edge[i]; 36 for(j=1;j<=t;j++) 37 { 38 f[y][j]=f[f[y][j-1]][j-1]; 39 dis[y][j]=dis[f[y][j-1]][j-1]+dis[y][j-1]; 40 } 41 } 42 } 43 } 44 45 long long lca(int x,int y) 46 { 47 long long ans=0; 48 if(d[x]>d[y]) swap(x,y); 49 for(int i=t;i>=0;i--) 50 if(d[f[y][i]]>=d[x]) ans+=dis[y][i],y=f[y][i]; 51 if(x==y) return ans; 52 for(int i=t;i>=0;i--) 53 if(f[x][i]!=f[y][i]) ans+=dis[x][i]+dis[y][i],x=f[x][i],y=f[y][i]; 54 return ans+dis[x][0]+dis[y][0]; 55 } 56 57 void dfs(int x) 58 { 59 v[x]=++tot,a[tot]=x; 60 for(int i=head[x];i;i=next[i]) 61 if(!v[ver[i]]) dfs(ver[i]); 62 } 63 64 inline It L(It it) 65 { 66 if(it==s.begin()) return --s.end(); 67 return --it; 68 } 69 70 inline It R(It it) 71 { 72 if(it==--s.end()) return s.begin(); 73 return ++it; 74 } 75 76 int main() 77 { 78 freopen("stone.in","r",stdin); 79 freopen("stone.out","w",stdout); 80 cin>>n; 81 t=(int)(log(n)/log(2)+0.1); 82 for(i=1;i<n;i++) 83 { 84 scanf("%d%d%d",&x,&y,&z); 85 add(x,y,z),add(y,x,z); 86 } 87 bfs(); 88 memset(v,0,sizeof(v)),tot=0; 89 dfs(1); 90 cin>>m; 91 for(i=1;i<=m;i++) 92 { 93 scanf("%s",str); 94 if(str[0]=='+') 95 { 96 scanf("%d",&x); 97 if(s.size()) 98 { 99 it=s.lower_bound(v[x]); 100 if(it==s.end()) it=s.begin(); 101 y=*L(it); 102 ans+=lca(x,a[y])+lca(x,a[*it])-lca(a[y],a[*it]); 103 } 104 s.insert(v[x]); 105 } 106 if(str[0]=='-') 107 { 108 scanf("%d",&x); 109 it=s.find(v[x]); 110 y=*L(it),it=R(it); 111 ans-=lca(x,a[y])+lca(x,a[*it])-lca(a[y],a[*it]); 112 s.erase(v[x]); 113 } 114 if(str[0]=='?') printf("%lld ",ans/2); 115 } 116 return 0; 117 }
序列变换
(change.pas/c/cpp)
题目描述
给定一个长度为N的数列Ai。
你可以对数列进行若干次操作,每次操作可以从数列中任选一个数,把它移动到数列的开头或者结尾。
求最少经过多少次操作,可以把数列变成单调不减的。“单调不减”意味着数列中的任意一个数都不大于排在它后边的数。
输入格式
第一行是一个正整数N。
第二行是N个正整数Ai。
输出格式
输出一个整数,表示最少需要的操作次数。
样例输入
5
6 3 7 8 6
样例输出
2
数据范围与约定
对于30%的数据,满足1≤n≤10。
对于60% 的数据,满足1≤n≤1000。
对于100% 的数据,满足1≤n≤1000000,1≤Ai≤1000000。
Solution:
之前国庆的校内模拟F大爷出过几乎一模一样的题目(别找以前的blog啦,没更新der),唯一的不同在于这次的题目有重复。
很显然,这是一个LIS问题。
存在两个条件:
1.若子序列中的最小数是L,最大数是R,则子序列里面存在[L+1,R-1]中的所有数。
2.子序列单调递增。
我们用单调队列维护一个dp,求出满足这两个条件的子序列的最长长度,用n减去就是答案。
即保持这个子序列不动,其余数向前或向后移动一次。
/*********************************************************************************************************************************************************************************/
其实上面的都是假的solution(以及下面的std)。
这道题的时限是1s,而该做法(即std)在test 9 and test 10 分别是1.4s and 1.2s。
所以如果要在1s内应该是dp+基数排序(但是由于我很弱所以我订正的也只能是单调队列惹)
如果有神犇看到了请不要D~
//F大爷的做法是满分的,暂且贴在这儿(等待日后更新~)
Other:
真的是原题啊!
考场上按照之前的思路魔改了一下,竟然也有五十分(就悄悄的放在这惹)
1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define MAXN 1000005 5 using namespace std; 6 inline int read(){ 7 int x=0,f=1;char ch=getchar(); 8 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 9 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 10 return x*f; 11 } 12 int n,ans=0,f[MAXN],F[MAXN]; 13 inline int Max(int a,int b){return a>b?a:b;} 14 int main(){ 15 freopen("change.in","r",stdin); 16 freopen("change.out","w",stdout); 17 n=read(); 18 memset(f,0,sizeof(f));memset(F,0,sizeof(F)); 19 for(int i=1;i<=n;++i){ 20 int x=read(),mx=f[x-1]+(++F[x]); 21 ans=Max(ans,mx); 22 f[x]=mx; 23 } 24 cout<<n-ans; 25 return 0; 26 }
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #include<map> 7 #include<iomanip> 8 #include<queue> 9 #include<set> 10 using namespace std; 11 const int SIZE = 1000010; 12 int a[SIZE], q[SIZE]; 13 int n, m, ans; 14 vector<int> b[SIZE]; 15 16 int main() 17 { 18 freopen("change.in","r",stdin); 19 freopen("change.out","w",stdout); 20 cin >> n; 21 for (int i = 1; i <= n; i++) 22 { 23 scanf("%d", &a[i]); 24 b[a[i]].push_back(i); 25 m = max(m, a[i]); 26 } 27 int l = 1, r = 0; 28 for (int i = 1; i <= m; i++) 29 { 30 reverse(b[i].begin(), b[i].end()); 31 for (int j = 0; j < b[i].size(); j++) 32 { 33 int k = b[i][j]; 34 while (l <= r && q[r] > k) 35 { 36 while (l < r && a[q[l]] < a[q[r]]) l++; 37 r--; 38 } 39 ans = max(ans, r - l + 2 + j); 40 } 41 for (int j = b[i].size() - 1; j >= 0; j--) 42 { 43 q[++r] = b[i][j]; 44 } 45 //cout << ans << endl; 46 } 47 cout << n - ans << endl; 48 }
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #define MN 1000000 5 using namespace std; 6 inline int read() 7 { 8 int x=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} 11 return x*f; 12 } 13 int n,a[MN+5],num[MN+5],Num[MN+5],f[MN+5][2][2],last[MN+5],ans=0; 14 int main() 15 { 16 freopen("change.in","r",stdin); 17 freopen("change.out","w",stdout); 18 n=read(); 19 memset(f,200,sizeof(f)); 20 for(int i=1;i<=n;++i) ++num[a[i]=read()]; 21 for(int i=1,j=0;i<=MN;++i) last[i]=j,j=num[i]?i:j; 22 for(int i=1;i<=n;++i) 23 { 24 if(!Num[a[i]]) f[a[i]][0][1]=1; 25 else for(int j=0;j<2;++j)for(int k=0;k<2;++k)++f[a[i]][k][j]; 26 if(last[a[i]]) 27 { 28 // cout<<i<<" "<<last[a[i]]<<endl; 29 int fg=bool(Num[a[i]])^1; 30 for(int j=0;j<2;++j)for(int k=0;k<2;++k) 31 if(!(j&&(!k||Num[last[a[i]]]<num[last[a[i]]]))) 32 f[a[i]][1][fg]=max(f[a[i]][1][fg],f[last[a[i]]][j][k]+1); 33 } 34 for(int j=0;j<2;++j) for(int k=0;k<2;++k) ans=max(ans,f[a[i]][j][k]); 35 ++Num[a[i]]; 36 } 37 printf("%d ",n-ans); 38 return 0; 39 }