NOIP2013 Day1
1.转圈游戏
https://www.luogu.org/problem/show?pid=1965
这道题失误极大,把freopen注释掉了,导致第一题暴0.
注意:在考试时一定要留下最后的时间检查格式!!!
由于此题中k的值很大,所以暴力只能过80%数据,需要用到快速幂。
#include <cstdio> long long n,m,k,x,ans,t; int main() { freopen("circle.in","r",stdin); freopen("circle.out","w",stdout); scanf("%lld%lld%lld%lld",&n,&m,&k,&x); ans=1; t=10; while(k) { if(k%2==1)ans=(ans*t)%n; k/=2; t=((t%n)*(t%n))%n; } printf("%d",(x+(m*ans)%n)%n); fclose(stdin); fclose(stdout); return 0; }
2.火柴排队
https://www.luogu.org/problem/show?pid=1966
这道题可用树状数组||逆序对||归并排序完成
献上树状数组代码:
#include<cstdio> #include<algorithm> using namespace std; struct MyStruct { int data; int loc; }a[100010],b[100010]; int e[100010], n, c[100010]; int inline readint() { int x = 0; char c = getchar(); while (c<'0' || c>'9') c = getchar(); while (c >= '0'&&c <= '9') { x = x * 10 + c - '0'; c = getchar(); } return x; } int lowbit(int x) { return x&-x;//树状数组实现 } void add(int x,int t) { while (x <= n) { e[x] += t; e[x] %=99999997; x += lowbit(x);//每次往后加,可以改变后面对应的和 } } int sum(int x) { int s = 0; while(x) { s += e[x]; s %= 99999997; x -= lowbit(x);//得到所求的和 } return s; } bool cmp(MyStruct x, MyStruct y) { return x.data < y.data; } int main() { n = readint(); for (int i = 1; i <= n; i++) { a[i].data = readint(); a[i].loc = i;//记录位置 } for (int i = 1; i <= n; i++) { b[i].data = readint(); b[i].loc = i; } sort(a + 1, a + n + 1, cmp); sort(b + 1, b + n + 1, cmp); for (int i = 1; i <= n; i++) { c[a[i].loc] = b[i].loc;//离散优化 } int ans = 0; for (int i = 1; i <= n; i++) { add(c[i], 1);//离散优化后大小就是正确顺序的位置 ans += i - sum(c[i]);//当前位置,减去之前比他大的数的个数 ans %= maxm; } printf("%d", ans); return 0; }
3.货车运输
本题用LCA+最大生成树完成。。。
#include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #define MAXN 10005 using namespace std; int n,m; struct T { int v; int w; int next; }edge[100005]; struct P { int u; int v; int w; }a[MAXN*5]; bool cmp(P x,P y) { return x.w > y.w; } int head[MAXN],cnt; int f[MAXN]; int find(int x)//并查集,判断是否在同一个集合内 { if(f[x] == x) return f[x]; else return f[x] = find(f[x]); } void Add_edge(int u,int v,int w)//树连边 { edge[cnt].v = v; edge[cnt].w = w; edge[cnt].next = head[u]; head[u] = cnt++; } void Union(int u,int v)//联通块 { int x = find(u); int y = find(v); if(x != y) f[x] = y; } void kruskal()//最大生成树 { for(int i = 0; i <= MAXN; i++) f[i] = i; for(int i = 1; i <= m; i++) { int u = a[i].u,v = a[i].v; if(find(u) != find(v)) { Union(u,v); Add_edge(u,v,a[i].w); Add_edge(v,u,a[i].w); } } } int up[MAXN][25],g[MAXN][25],h[MAXN];//up[i][j]表示i的第2^j个祖先,g[i][j]表示i到i的第2^j个祖先路径上的最小权值,h[i]表示i在树中深度 bool vis[MAXN]; void build_tree(int u) { vis[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next) { int v = edge[i].v; if(!vis[v]) { g[v][0] = edge[i].w; up[v][0] = u; h[v] = h[u]+1; build_tree(v); } } } //把较深的一个点往上提,并记录他到祖先边权最小值,用他的一个祖先代替他 int move(int &u,int H) { int res = 123546789; for(int i =20; i >= 0; i--) { if(h[up[u][i]] >= H) { res = min(res,g[u][i]); u = up[u][i]; } } return res; } int query(int u,int v)//自认为是最难的地方 { if(find(u) != find(v)) return -1; int res = 123456789; if(h[u] != h[v]) res = h[u] > h[v]?move(u,h[v]):move(v,h[u]); if(u == v) return res; for(int i = 20; i >= 0; i--)//倍增的同时记录最小值,两个点越来越逼近公共祖先 { if(up[u][i] != up[v][i]) { res = min(res,min(g[u][i],g[v][i])); u = up[u][i]; v = up[v][i]; } } res = min(res,min(g[u][0],g[v][0]));//实际上到了这一步up[x][0] == up[y][0]因为它们的已经在同一棵子树里面 //printf("up[u][0]: %d\n",up[u][0]); //printf("up[v][0]: %d\n",up[v][0]); return res; } int main() { memset(head,-1,sizeof head); scanf("%d%d",&n,&m); for(int i = 1; i <= m; i++) scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w); sort(a+1,a+m+1,cmp); kruskal(); for(int i = 1; i <= n; i++)//构建森林,并且初始化h,up,g { if(!vis[i]) { h[i] = 0; build_tree(i); g[i][0] = 123456789; up[i][0] = i; } } for(int i = 1; i <= 20; i++)//预处理up和g,i大了也没有什么影响 { for(int j = 1; j <= n; j++) { up[j][i] = up[up[j][i-1]][i-1]; g[j][i] = min(g[j][i-1],g[up[j][i-1]][i-1]); } } int q; scanf("%d",&q); for(int i = 1; i <= q; i++) { int x,y; scanf("%d%d",&x,&y); printf("%d\n",query(x,y)); } return 0; }