NENU ACM 13级训练赛 2014-12-06(福州大学第十一届程序设计竞赛)
2014-12-06 17:17
260 查看
福大的校赛,题目是中文的,省去了大家读题的时间。
A题:水题
每7天就有一个周六和周日,算一算就行了。
B题:规律
类似单调队列,其实就是个O(n)的递推水题,把样例推一推就清楚了
2 1 3 1 4
2*1 + 1*2 + 3*3 (1)
1*1 + 3*2 + 1*3 (2)
3*1 + 1*2 + 4*3 (3)
式子(1)为 13
式子(2)为 13 - 2*1 - (1+3) + 1*3 = 10
即: 13 - (2+1+3) + 1*3 = 10
式子(3)为 10 - 1*1 - (3+1) + 4*3 = 17
即: 10 - (1+3+1) + 4*3 = 17
可以发现,式子(i)式子(i-1)减去sum(x[i-m]~x[i])再加上新的项。
这样扫一遍取最大就可以了
C题:DFS
由题可知,给出的图是一颗树。
设1为根,我们都知道对于树,若一条边只能走一次,则从根到各个节点的路径是唯一的,而且是最短的。
用邻接表建图,从根开始深搜进去,遍历每一个节点,在回溯的过程中如果是军队要走的路径,就把节点标记一下。
最后把这些被标记的结点上的敌人求个和,就是答案。
D:DP
动态规划的重点就是定义出状态,已经状态的变化,据此写出状态转移方程之后,问题就迎刃而解了。
定义dp[i][j]表示到第i位时,有j位属于第一个串。
那么对于第i位的放法,就有放在第一个串和不放在第一个串这两种选择,然后更新数组,推到下一个状态。
注意推的过程中要模mod
为了节省空间,我采用了滚动数组,大概不用滚动数组也是能AC的,可以试试。
对于这个题目转移思想和滚动数组的使用,建议认真研究一下《背包九讲》
E:线段树
很明显的线段树的题,采用lazy数组延迟标记,区间查询区间更新。
属于基础题,望多加巩固练习。
题目说有30%的小数据,可能暴力能过吧,我不清楚,我看见有人裸暴TLE,还是稳一点吧。
F题:待更新
G题:多源最短路+矩阵快速幂
题目要求从1点到n点的恰好k次最短路
可以很快想到floyd求出各个点之间的最短路,但是k很大,不能递推过去。
可以先求出1秒之后,各个点之间的最短路,得出一个矩阵,然后我们可以用这个矩阵继续推下去,推k-1次,这样就可以想到用矩阵快速幂了。
其实在这里,矩阵快速幂起到了加速的效果。
注意本题的矩阵快速幂并不是常规的矩阵快速幂,需要改写一下,具体看代码。
H题:待更新
A题:水题
每7天就有一个周六和周日,算一算就行了。
<span style="font-size:18px;">#include<iostream> #include<cmath> #include<cstring> #include<cstdio> using namespace std; int main(){ int n; while(~scanf("%d",&n)){ int num = n/7; int left = n%7; int ans = num*2; if(left == 6) ans++; printf("%d\n",ans); } return 0; }</span>
B题:规律
类似单调队列,其实就是个O(n)的递推水题,把样例推一推就清楚了
2 1 3 1 4
2*1 + 1*2 + 3*3 (1)
1*1 + 3*2 + 1*3 (2)
3*1 + 1*2 + 4*3 (3)
式子(1)为 13
式子(2)为 13 - 2*1 - (1+3) + 1*3 = 10
即: 13 - (2+1+3) + 1*3 = 10
式子(3)为 10 - 1*1 - (3+1) + 4*3 = 17
即: 10 - (1+3+1) + 4*3 = 17
可以发现,式子(i)式子(i-1)减去sum(x[i-m]~x[i])再加上新的项。
这样扫一遍取最大就可以了
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; const int MAXN = 1000010; int a[MAXN]; int sum[MAXN]; int n,m; int main() { while(~scanf("%d%d",&n,&m)) { sum[0] = 0; for(int i=1; i<=n; i++) { scanf("%d",&a[i]); sum[i] = sum[i-1] + a[i]; } int ans = -1; int tmp = 0; for(int i=1; i<=m; i++) { tmp += i*a[i]; } ans = max(ans,tmp); for(int i=m+1; i<=n; i++) { tmp = tmp - (sum[i-1]-sum[i-m-1]) + m*a[i]; ans = max(tmp,ans); } printf("%d\n",ans); } return 0; }</span>
C题:DFS
由题可知,给出的图是一颗树。
设1为根,我们都知道对于树,若一条边只能走一次,则从根到各个节点的路径是唯一的,而且是最短的。
用邻接表建图,从根开始深搜进去,遍历每一个节点,在回溯的过程中如果是军队要走的路径,就把节点标记一下。
最后把这些被标记的结点上的敌人求个和,就是答案。
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #define mt(a,b) memset(a,b,sizeof(a)) using namespace std; const int M = 100100; bool vis[M]; int val[M]; struct G{ struct E{ int v,next; }e[M<<1]; int le,head[M]; void init(){ le=0; mt(head,-1); } void add(int u,int v){ e[le].v=v; e[le].next=head[u]; head[u]=le++; } }g; int fa[M]; void dfs(int u){ for(int i=g.head[u];~i;i=g.e[i].next){ int v = g.e[i].v; if(fa[u]==v) continue; fa[v] = u; dfs(v); if(vis[v]){ vis[u]=true; } } } int main(){ int n,k; while(~scanf("%d%d",&n,&k)){ mt(vis,false); mt(fa,-1); g.init(); for(int i=1;i<=n;i++){ scanf("%d",&val[i]); } int tmp; for(int i=1;i<=k;i++){ scanf("%d",&tmp); vis[tmp] = true; } int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); g.add(x,y); g.add(y,x); } dfs(1); int sum = 0; for(int i=1;i<=n;i++){ if(vis[i]){ sum += val[i]; } } printf("%d\n",sum); } return 0; }</span>
D:DP
动态规划的重点就是定义出状态,已经状态的变化,据此写出状态转移方程之后,问题就迎刃而解了。
定义dp[i][j]表示到第i位时,有j位属于第一个串。
那么对于第i位的放法,就有放在第一个串和不放在第一个串这两种选择,然后更新数组,推到下一个状态。
注意推的过程中要模mod
为了节省空间,我采用了滚动数组,大概不用滚动数组也是能AC的,可以试试。
对于这个题目转移思想和滚动数组的使用,建议认真研究一下《背包九讲》
#include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define LL __int64 #define mt(a,b) memset(a,b,sizeof(a)) using namespace std; const LL MOD = 1e9+7; const int M = 3010; char str[M]; LL dp[2][M]; int n; int main() { int _; scanf("%d",&_); while(_--) { scanf("%d",&n); scanf("%s",str); mt(dp,0); dp[0][0] = 1; int now = 1; for(int i=0; i<2*n; i++) { mt(dp[now&1],0); if (str[i]=='B') { for (int j=0; j<=n; j++) { //奇数 if (j&1) { dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD; } if ((i-j)&1) { dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD; } } } else { for (int j=0; j<=n; j++) { if ((j&1) == 0) { dp[now&1][j+1] = (dp[now&1][j+1] + dp[i&1][j]) % MOD; } if (((i-j)&1) == 0) { dp[now&1][j] = (dp[now&1][j] + dp[i&1][j]) % MOD; } } } now++; } printf("%I64d\n",dp[0] ); } return 0; }
E:线段树
很明显的线段树的题,采用lazy数组延迟标记,区间查询区间更新。
属于基础题,望多加巩固练习。
题目说有30%的小数据,可能暴力能过吧,我不清楚,我看见有人裸暴TLE,还是稳一点吧。
#include<cstdio> #define lrrt int L,int R,int rt #define iall 1,n,1 #define imid int mid=(L+R)>>1 #define lson L,mid,rt<<1 #define rson mid+1,R,rt<<1|1 const int M=1e5+10; int a[M]; struct T { int sum,lazy; } tree[M<<2]; void pushup(int rt) { tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum; } void build(lrrt) { tree[rt].lazy=0; if(L==R) { tree[rt].sum=a[L]; return ; } imid; build(lson); build(rson); pushup(rt); } void pushdown(int mid,lrrt) { if(tree[rt].lazy) { tree[rt<<1].lazy+=tree[rt].lazy; tree[rt<<1|1].lazy+=tree[rt].lazy; tree[rt<<1].sum+=(mid-L+1)*tree[rt].lazy; tree[rt<<1|1].sum+=(R-mid)*tree[rt].lazy; tree[rt].lazy=0; } } int query(int x,int y,lrrt) { if(x<=L&&R<=y) return tree[rt].sum; imid; pushdown(mid,L,R,rt); int ans=0; if(mid>=x) ans+=query(x,y,lson); if(mid<y) ans+=query(x,y,rson); return ans; } void update(int x,int y,int z,lrrt) { if(x<=L&&R<=y) { tree[rt].sum+=(R-L+1)*z; tree[rt].lazy+=z; return ; } imid; pushdown(mid,L,R,rt); if(mid>=x) update(x,y,z,lson); if(mid<y) update(x,y,z,rson); pushup(rt); } int main() { int n,m,q,x; while(~scanf("%d%d%d",&n,&m,&q)) { for(int i=1; i<=n; i++) { scanf("%d",&a[i]); } build(iall); while(q--) { scanf("%d",&x); printf("%d\n",query(x,x+m-1,iall)); update(x,x+m-1,-1,iall); } } return 0; }
F题:待更新
G题:多源最短路+矩阵快速幂
题目要求从1点到n点的恰好k次最短路
可以很快想到floyd求出各个点之间的最短路,但是k很大,不能递推过去。
可以先求出1秒之后,各个点之间的最短路,得出一个矩阵,然后我们可以用这个矩阵继续推下去,推k-1次,这样就可以想到用矩阵快速幂了。
其实在这里,矩阵快速幂起到了加速的效果。
注意本题的矩阵快速幂并不是常规的矩阵快速幂,需要改写一下,具体看代码。
<span style="font-size:18px;">#include<iostream> #include<cstdio> #include<cmath> #include<cstring> #define LL __int64 #define mt(a,b) memset(a,b,sizeof(a)) using namespace std; const LL inf = 0x3f3f3f3f3f3f3f3fLL; class Matrix { typedef LL typev; static const int MV=55; friend Matrix operator *(const Matrix &a,const Matrix &b) { Matrix ret; ret.n=a.n; for(int i=0; i<a.n; i++) { for(int j=0; j<b.n; j++) { LL tmp = inf; for(int k=0; k<a.n; k++) { tmp = min(tmp, a.val[i][k] + b.val[k][j]); } ret.val[i][j]=tmp; } } return ret; } friend Matrix operator ^ (Matrix a,LL b) { Matrix ret = a; while(b) { if(b&1) ret = a*ret; a = a*a; b>>=1; } return ret; } public: int n,m;///n行m列 typev val[MV][MV]; void init(int tn) { mt(val,inf); n = tn; } } A; int main() { int n,h; LL k; int _; scanf("%d",&_); while(_--) { scanf("%d%d%I64d",&n,&h,&k); A.init(n); int u,v; LL w; while(h--) { scanf("%d%d%I64d",&u,&v,&w); u--; v--; A.val[u][v] = min(A.val[u][v],w); } Matrix ans = A^(k-1); if(ans.val[0][n-1] == inf) puts("-1"); else printf("%I64d\n",ans.val[0][n-1]); } return 0; } </span>
H题:待更新
相关文章推荐
- 福州大学第十一届程序设计竞赛
- 福州大学第十一届程序设计竞赛
- 福州大学第十一届程序设计竞赛
- 福州大学第十一届程序设计竞赛 部分题目题解
- 福州大学第十一届程序设计竞赛
- 福州大学第十一届程序设计竞赛E fzuoj2171(线段树)
- 2015.10.20 福州大学第九届程序设计竞赛
- 2014 第十一届浙江省程序设计竞赛
- 中南大学第十一届大学生程序设计竞赛总结
- 2017第十一届东北地区程序设计竞赛总结2017/5/14
- 福州大学第十二届程序设计竞赛-G - Escape
- 请你帮帮小王 (长沙理工大学第十一届程序设计竞赛)
- FZU-2231 平行四边形数 From 福州大学第十三届程序设计竞赛
- FZU 2254 英语考试 (最小生成树)(福州大学第十四届程序设计竞赛)
- 第十一届北京师范大学程序设计竞赛(网络同步赛)+Adidas and adivon
- HNNU 11658 阶乘除法【湖南省第十一届大学生计算机程序设计竞赛,数论】
- 第十一届“蓝狐网络杯”湖南省大学生计算机程序设计竞赛
- 河南省第十一届ACM大学生程序设计竞赛
- 中南大学第十一届大学生程序设计竞赛网络预选赛总结
- 中南大学第十一届大学生程序设计竞赛-COJ1899-Yuelu Scenes