[校内模拟题4]
“与”
(and.pas/.c/.cpp)时间限制:1s;空间限制 64MB
题目描述:
给你一个长度为 n 的序列 A,请你求出一对 Ai,Aj(1<=i<j<=n)使 Ai“与”Aj 最大
Ps:“与”表示位运算 and,在 c++中表示为&。
输入描述:
第一行为 n。接下来 n 行,一行一个数字表示 Ai。
输出描述:
输出最大的 Ai“与”Aj 的结果。
样例输入:
3
8
10
2
样例输出:
8
样例解释:
8 and 10 = 8
8 and 2 = 0
10 and 2 = 2
数据范围:
20%的数据保证 n<=5000100%的数据保证 n<=3*10^5,0<=Ai<=10^9
——————————————————————————————————————————————————————————————
这道题主要是对二进制性质的一些应用,有体现“正难则反”的思想。20分的暴力是很简单的枚举。然后对于另外80分,我们考虑:“与”是要求同一位均是1,结果才为1。而对于2进制,越高位有1,数字越大。所以我们可以考虑将所给数列的数字看成二进制,从二进制最高位向下枚举,若有大于等于两个数“这一位上为1”,那么最终结果的这一位肯定是1,于是我们就可以将所有“这一位上为1”的数保留下来,剩下的数舍弃;而若有少于两个数“这一位上为1”,则这一位上一定为0。
stdcode
#include<cstdio> #include<cstring> #include<cstdlib> #include<iostream> #include<cmath> #include<algorithm> using namespace std; int n,a[300002],d[35],ans;//2^30>1000000000 void init() { scanf("%d",&n); int i; for(i=1;i<=n;i++) scanf("%d",&a[i]); } void find() { int i,j,ct,now=n,t; for(i=30;i>=0;i--) {ct=0; for(j=1;j<=now;j++) {if(a[j]&(1<<i)) ct++;} if(ct<2) continue; t=0; d[i]=1; for(j=1;j<=now;j++) {if(a[j]&(1<<i)) a[++t]=a[j];} now=t; } for(i=30;i>=0;i--) {if(d[i]) ans=ans^(1<<i); } printf("%d\n",ans); } int main() { freopen("and.in","r",stdin); freopen("and.out","w",stdout); init(); find(); return 0; }
小象涂色
(elephant.pas/.c/.cpp)时间限制:1s,空间限制 128MB
题目描述:
小象喜欢为箱子涂色。小象现在有 c 种颜色,编号为 0~c-1;还有 n 个箱子,编号为1~n,最开始每个箱子的颜色为 1。小象涂色时喜欢遵循灵感:它将箱子按编号排成一排,每次涂色时,它随机选择[L,R]这个区间里的一些箱子(不选看做选 0 个),为之涂上随机一种颜色。若一个颜色为 a 的箱子被涂上 b 色,那么这个箱子的颜色会变成(a*b)modc。请问在 k 次涂色后,所有箱子颜色的编号和期望为多少?
输入描述:
第一行为 T,表示有 T 组测试数据。对于每组数据,第一行为三个整数 n,c,k。接下来 k 行,每行两个整数 Li,Ri,表示第 i 个操作的 L 和 R。
输出描述:
对于每组测试数据,输出所有箱子颜色编号和的期望值,结果保留 9 位小数。
样例输入:
3
3 2 2
2 2
1 3
1 3 1
1 1
5 2 2
3 4
2 4
样例输出:
2.062500000
1.000000000
3.875000000
数据范围:
40%的数据 1 <= T <= 5,1 <= n, k <= 15,2 <= c <= 20
100%的数据满足 1 <= T <= 10,1 <= n, k <= 50,2 <= c <= 100,1 <= Li <= Ri <= n
——————————————————————————————————————————————————————————————
一道有关期望的dp。首先可知每个操作中,一个物品会被染色的概率为1/2,用某种颜色染色的概率为1/c。
40分的方程是用f[i][j][k]表示第i个物品在j次操作次数后颜色变为k的概率,时间复杂度大概是$O(T*N*K*c^2)$
60分要考虑到所有物品具有相似性,即n个物品本质是相同的,所以不用枚举物品f[i][j]表示一个物品操作i次颜色变为j的概率。满足:
$f[i+1][j] += f[i][j]*(\frac{1}{2})$
$f[i+1][(j*b) \mod c] += f[i][j]*[(\frac{1}{2})*(\frac{1}{c})]$
初始值$f[0][1]=1$,答案就是$\sum\limits_{j=0}^{c-1}f[i][j]*j$(i表示该箱子的操作次数。复杂度$O(T*K*c^2)$
然而数组开小了得了60分
code
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> using namespace std; typedef double db; int T, n, c, k; int t[50]; db f[50][100], ans; //db f[50][50], ans; inline int read() { int num = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) { num = num * 10 + ch - '0'; ch = getchar(); } return num * f; } int main() { freopen("elephant.in", "r", stdin); freopen("elephant.out", "w", stdout); T = read(); while (T -- ) { memset(f, 0, sizeof(f)); memset(t, 0, sizeof(t)); ans = 0; n = read(); c = read(); k = read(); f[0][1] = 1.0; f[1][1] += 0.5*f[0][1]; for (int i = 0; i < c; ++ i) f[1][i % c] += f[0][1] * 0.5 / c; for (int i = 1; i <= k; ++ i) for (int j = 0; j < c; ++ j) { f[i + 1][j] += 0.5 * f[i][j]; for (int k = 0; k < c; ++ k) f[i + 1][j * k % c] += f[i][j] * 0.5 / c; } for (int i = 1; i <= k; ++ i) t[read()]++, t[read() + 1]--; for (int i = 1; i <= n; ++ i) t[i] += t[i - 1]; for (int i = 1; i <= n; ++ i) for (int j = 0; j < c; ++ j) ans += f[t[i]][j] * j; printf("%.9lf\n", ans); } return 0; }
行动!行动!
(move.pas/.c/.cpp)时间限制:1s;空间限制:128MB
题目描述:
大 CX 国的大兵 Jack 接到一项任务:敌方占领了 n 座城市(编号 0~n-1),有些城市之间有双向道路相连。Jack 需要空降在一个城市 S,并徒步沿那些道路移动到 T 城市。虽然Jack 每从一个城市到另一个城市都会受伤流血,但大 CX 国毕竟有着“过硬”的军事实力,它不仅已经算出 Jack 在每条道路上会损失的血量,还给 Jack 提供了 k 个“简易急救包”,一个包可以让 Jack 在一条路上的流血量为 0。Jack 想知道自己最少会流多少血,不过他毕竟是无脑的大兵,需要你的帮助。
输入描述:
第一行有三个整数 n,m,k,分别表示城市数,道路数和急救包个数。第二行有两个整数,S,T。分别表示 Jack 空降到的城市编号和最终要到的城市。接下来有 m 行,每行三个整数 a,b,c,表示城市 a 与城市 b 之间有一条双向道路。
输出描述:
Jack 最少要流的血量。
样例输入:
5 6 10 3
3 4 5
0 1 5
0 2 100
1 2 5
2 4 5
2 4 3
样例输出:
8
数据范围:
对于 30%的数据,2<=n<=50,1<=m<=300,k=0;对于 50%的数据,2<=n<=600,1<=m<=6000,0<=k<=1;对于 100%的数据,2<=n<=10000,1<=m<=50000,0<=k<=10.
——————————————————————————————————————————————————————————————
可以用一个二维的dis数组跑最短路,dis[i][j]表示到达i点,用了j次急救包。用最短路做一个类似于dp的东西.
code
#include<cstdio> #include<cstring> #include<queue> #include<deque> #include<algorithm> #include<iostream> #define mp(x, y) make_pair(x, y) #define qff q.front().first #define qfs q.front().second using namespace std; typedef pair<int, int> pir; const int INF = 0x3f3f3f3f; const int MAXN = 10010; const int MAXM = 50010; int head[MAXM * 2], tot; bool vis[MAXN]; int dis[MAXN][20]; int n, m, k, S, T; deque<pir >q; struct Edge { int to; int nxt; int val; }e[MAXM * 2]; inline int read() { int num = 0, f = 1; char ch = getchar(); while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); } while (isdigit(ch)) { num = num * 10 + ch - '0'; ch = getchar(); } return num * f; } inline void Add_Edge(int x,int y,int z) { e[++tot].to = y; e[tot].val = z; e[tot].nxt = head[x]; head[x] = tot; } void pre(int s) { for (int i = 1; i <= n; ++ i) for (int j = 0; j <= k; ++ j) dis[i][j] = INF; for (int i = 0; i <= k; ++ i) dis[s][i] = 0; memset(vis,0,sizeof(vis)); } void SPFA(pir s) { q.push_front(s); int xx = s.first, Usee, v; vis[xx] = true; pir h; while (!q.empty()) { h = q.front(); q.pop_front() ; xx = h.first; Usee = h.second; vis[xx] = false; for (int i = head[xx]; i ; i = e[i].nxt) { v = e[i].to; if (dis[v][Usee] > dis[xx][Usee] + e[i].val) { dis[v][Usee] = dis[xx][Usee] + e[i].val; if (q.empty() || dis[v][Usee] > dis[qff][qfs]) q.push_back(mp(v, Usee)); else q.push_front(mp(v, Usee)); } if (Usee < k && dis[xx][Usee] < dis[v][Usee + 1]) { dis[v][Usee + 1] = dis[xx][Usee]; if (q.empty() || dis[v][Usee + 1] < dis[qff][qfs]) q.push_back(mp(v, Usee + 1)); else q.push_front(mp(v, Usee + 1)); } } } } int main() { n = read(); m = read(); k = read(); S = read() + 1; T = read() + 1; for (int i = 1; i <= m; i++) { int a = read() + 1, b = read() + 1, c = read(); Add_Edge(a, b, c); Add_Edge(b, a, c); } pre(S); SPFA(mp(S, 0)); int ans = INF; for (int i = 0; i <= k; ++ i) ans = min(ans, dis[T][i]); cout << ans << endl; return 0; }