CSU-ACM暑假集训基础组七夕专场

•Problem A Codeforces 20C       最短路(dij,spfa)
•题意:给出一张n个点m条边的无向图 (2 ≤ n ≤ 105, 0 ≤ m ≤ 105),输出从1到n的任意一条最短路径。
•解法:dijkstra或者spfa,用pre数组记录到达每个点最短距离的前驱结点。
•注意:路径的长度范围是 1 ≤ wi ≤ 106,从1到n的最短路径长度可能超出int范围。
•没有路径从1到达n时要输出-1
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <queue>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long LL;
 7 typedef pair<LL,int> pii;
 8 const int maxn = 100010;
 9 const int maxm = 200010;
10 int v[maxm],next[maxm],w[maxm];
11 int first[maxn],pre[maxn],res[maxn],e;
12 LL d[maxn];
13 
14 void init(){
15     e = 0;
16     memset(first,-1,sizeof(first));
17 }
18 
19 void add_edge(int a,int b,int c){
20     v[e] = b;next[e] = first[a];w[e] = c;first[a] = e++;
21 }
22 
23 void dijkstra(int src){
24     priority_queue <pii,vector<pii>,greater<pii> > q;
25     memset(d,-1,sizeof(d));
26     d[src] = 0;
27     q.push(make_pair(0,src));
28     while(!q.empty()){
29         while(!q.empty() && q.top().first > d[q.top().second])  q.pop();
30         if(q.empty())   break;
31         int u = q.top().second;
32         q.pop();
33         for(int i = first[u];i != -1;i = next[i]){
34             if(d[v[i]] > d[u]+w[i] || d[v[i]] == -1){
35                 d[v[i]] = d[u]+w[i];
36                 q.push(make_pair(d[v[i]],v[i]));
37                 pre[v[i]] = u;
38             }
39         }
40     }
41 }
42 
43 int main(){
44     init();
45     int n,m,a,b,c;
46     scanf("%d%d",&n,&m);
47     for(int i = 0;i < m;i++){
48         scanf("%d%d%d",&a,&b,&c);
49         add_edge(a,b,c);
50         add_edge(b,a,c);
51     }
52     dijkstra(1);
53     int now = n,cnt = 0;
54     if(d[n] == -1){
55         printf("-1");
56         return 0;
57     }
58     while(now != 1){
59         res[cnt++] = now;
60         now = pre[now];
61     }
62     res[cnt++] = 1;
63     for(int i = cnt-1;i >= 0;i--)  printf("%d ",res[i]);
64     return 0;
65 }
View Code

 

•Problem B Codeforces 295B     最短路(floyd)
•题意:对于一个带权有向图,每次删除一个点,要求计算出删除该点前当前图的所有点间的最短距离之和。
•首先,想知道任意两点间最短距离之和应该使用什么算法?
•毫无疑问 Floyd!
•对于删除顺序,我们可以怎么处理?
•可以反过来做。这样就相当于每次添加一个点到图中,然后询问当前图的所有点间最短距离之和。
•对于每次新增加的点v,我们用v对整张图进行松弛操作:d[s][t] = min(d[s][t],d[s][v]+d[v][t])
•对于统计操作,可以暴力枚举。
•整个复杂度为O(n^3)
•注意:本题数据范围可能超过int
 1 #include <cstdio>
 2 #include <cstring>
 3 #define maxn 510
 4 typedef long long LL;
 5 LL d[maxn][maxn];
 6 int seq[maxn];
 7 LL ans[maxn];
 8 
 9 inline LL min(LL a,LL b){
10     return a < b ? a : b;
11 }
12 
13 void update(int s,int n){
14     for(int i = 1;i <= n;i++)
15         for(int j = 1;j <= n;j++)
16             d[i][j] = min(d[i][s] + d[s][j],d[i][j]);
17 }
18 
19 LL query(int s,int n){
20     LL sum = 0;
21     for(int i = n,u = seq[i];i >= s;u = seq[--i])
22         for(int j = n,v = seq[j];j >= s;v = seq[--j])
23             sum += d[u][v];
24     return sum;
25 }
26 
27 int main()
28 {
29     int n;
30     while(scanf("%d",&n) != EOF){
31         for(int i = 1;i <= n;i++)
32             for(int j = 1;j <= n;j++)
33                 scanf("%I64d",&d[i][j]);
34         for(int i = 1;i <= n;i++)
35             scanf("%d",&seq[i]);
36         for(int i = n;i >= 1;i--){
37             update(seq[i],n);
38             ans[i] = query(i,n);
39         }
40         for(int i = 1;i <= n;i++)
41             printf("%I64d ",ans[i]);
42         printf("\n");
43     }
44     return 0;
45 }
View Code

 

•Problem C SPOJ MULTQ3           线段树
•题意:有一个长为N(N<=10^5)的数组,初始时数组每个元素的值都为0,接下来有Q(Q<=10^5)次操作,每次操作有2中类型:
•0 A B 将[A,B]区间内的所有数字+1
•1 A B 输出[A,B]区间内所有能够被3整除的数组元素的个数
•考察内容:
•线段树的基本应用
•区间维护
•Lazy标记的使用
•每次修改时,如果某个节点表示的区间[x,y]被加了1,那么我们要对这个区间的3个sum属性进行修改,同时在这个区间打上lazy标记,表示这个区间已经被修改了。
•当需要访问这个节点下面的节点的时候,一定会先经过这个点,这时候下传lazy标记,来保证子节点的信息是正确的。
 1 #include <cstdio>
 2 const int maxn = 100010;
 3 int sum[maxn<<2][3],add[maxn<<2];
 4 
 5 void pushup(int cur){
 6     int ls = cur<<1,rs = cur<<1|1;
 7     sum[cur][0] = sum[ls][0]+sum[rs][0];
 8     sum[cur][1] = sum[ls][1]+sum[rs][1];
 9     sum[cur][2] = sum[ls][2]+sum[rs][2];
10 }
11 
12 void pushdown(int cur){
13     int ls = cur<<1,rs = cur<<1|1,tmp;
14     if(add[cur] != 0){
15         add[ls] = (add[ls]+add[cur])%3;
16         add[rs] = (add[rs]+add[cur])%3;
17         if(add[cur] == 1){
18             tmp = sum[ls][2];
19             sum[ls][2] = sum[ls][1];
20             sum[ls][1] = sum[ls][0];
21             sum[ls][0] = tmp;
22         }else if(add[cur] == 2){
23             tmp = sum[ls][0];
24             sum[ls][0] = sum[ls][1];
25             sum[ls][1] = sum[ls][2];
26             sum[ls][2] = tmp;
27         }
28         if(add[cur] == 1){
29             tmp = sum[rs][2];
30             sum[rs][2] = sum[rs][1];
31             sum[rs][1] = sum[rs][0];
32             sum[rs][0] = tmp;
33         }else if(add[cur] == 2){
34             tmp = sum[rs][0];
35             sum[rs][0] = sum[rs][1];
36             sum[rs][1] = sum[rs][2];
37             sum[rs][2] = tmp;
38         }
39         add[cur] = 0;
40     }
41 }
42 
43 void bulid(int cur,int x,int y){
44     int mid = (x+y)>>1,ls = cur<<1,rs = cur<<1|1;
45     if(x == y){
46         sum[cur][0] = 1;
47         return;
48     }
49     bulid(ls,x,mid);
50     bulid(rs,mid+1,y);
51     pushup(cur);
52 }
53 
54 void update(int cur,int x,int y,int s,int t){
55     int mid = (x+y)>>1,ls = cur<<1,rs = cur<<1|1;
56     if(x >= s && y <= t){
57         int tmp = sum[cur][2];
58         sum[cur][2] = sum[cur][1];
59         sum[cur][1] = sum[cur][0];
60         sum[cur][0] = tmp;
61         add[cur] = (add[cur]+1)%3;
62         return;
63     }
64     pushdown(cur);
65     if(mid >= s)    update(ls,x,mid,s,t);
66     if(mid+1 <= t)  update(rs,mid+1,y,s,t);
67     pushup(cur);
68 }
69 
70 void query(int cur,int x,int y,int s,int t,int &ans){
71     int mid = (x+y)>>1,ls = cur<<1,rs = cur<<1|1;
72     if(x >= s && y <= t){
73         ans += sum[cur][0];
74         return;
75     }
76     pushdown(cur);
77     if(mid >= s)    query(ls,x,mid,s,t,ans);
78     if(mid+1 <= t)  query(rs,mid+1,y,s,t,ans);
79 }
80 
81 int main(){
82     int n,q,op,a,b;
83     scanf("%d%d",&n,&q);
84     bulid(1,0,n-1);
85     while(q--){
86         scanf("%d%d%d",&op,&a,&b);
87         if(op == 0) update(1,0,n-1,a,b);
88         else{
89             int ans = 0;
90             query(1,0,n-1,a,b,ans);
91             printf("%d\n",ans);
92         }
93     }
94     return 0;
95 }
View Code

 

•Problem D Codeforces 4C          Map
•题意:输入一个单词,查询字典内是否含有这个单词,如果没有,输出“OK”,将这个新单词插入字典中。如果有,输出做这个单词,后面紧接着输出这个单词出现的次数。
•考察内容:Map的使用
 1 #include <iostream>
 2 #include <string>
 3 #include <map>
 4 using namespace std;
 5 map<string,int> m;
 6 
 7 int main(){
 8     int n,a;
 9     string str;
10     cin>>n;
11     while(n--){
12         cin>>str;
13         a = m[str]++;
14         if(a)   cout<<str<<a<<endl;
15         else    cout<<"OK"<<endl;
16     }
17     return 0;
18 }
View Code

 

•Problem E SPOJ ROCK                 DP
•题意:有一块n个单位长的糖,有的地方是甜的(1),有的地方是酸的(0)。你可以将整块糖任意切成若干段。小盆友们只会买走甜的长度比酸的长度多的那段糖。问经过切割后,最多可以卖掉多少单位长度的糖。
•考察知识:DP
•dp(i)表示将前i段糖进行切割后最多能卖掉的数量
•状态转移方程:
•考虑在第j段之前切一刀
•dp(i)=max{ dp(i) , dp(j-1) +i–j+1}
•( 1 <= j <= i 且在[j,i]区间内甜的比酸的多)
 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 const int maxn = 205;
 6 char str[maxn];
 7 int dp[maxn],sum[maxn][2];
 8 
 9 int main(){
10     int T,len;
11     scanf("%d",&T);
12     while(T--){
13         scanf("%d %s",&len,str+1);
14         sum[0][0] = sum[0][1] = 0;
15         for(int i = 1;i <= len;i++){
16             sum[i][0] = sum[i-1][0];
17             sum[i][1] = sum[i-1][1];
18             sum[i][str[i]-'0']++;
19         }
20         dp[0] = 0;
21         for(int i = 1;i <= len;i++){
22             dp[i] = dp[i-1];
23             for(int j = 1;j <= i;j++){
24                 int sum0 = sum[i][0]-sum[j-1][0];
25                 int sum1 = sum[i][1]-sum[j-1][1];
26                 if(sum1 <= sum0)    continue;
27                 dp[i] = max(dp[i],dp[j-1]+i-j+1);
28             }
29         }
30         printf("%d\n",dp[len]);
31     }
32     return 0;
33 }
View Code

 

•Problem F SPOJ NITK06              模拟
•题意:给出一个长为n的数列ai(1<=i<=n),每个元素都非负。每次你可以选择一个下标x(1<=x<=n-1),将ax和ax+1同时减1。问经过若干次操作,能否将原数列变成一个元素全部为0的序列。
•模拟即可。
•从数列第一个元素开始,用它剩下的值减去下一个元素的值
•最后如果所有元素都能变为0,则满足条件。
 1 #include <cstdio>
 2 int a[10010];
 3 
 4 int main(){
 5     int T,n;
 6     scanf("%d",&T);
 7     while(T--){
 8         scanf("%d",&n);
 9         for(int i = 0;i < n;i++)    scanf("%d",&a[i]);
10         if(n == 1){
11             if(a[0] == 0)   printf("YES\n");
12             else            printf("NO\n");
13             continue;
14         }
15         bool flag = 0;
16         for(int i = 0;i < n-1;i++){
17             if(a[i] < 0){
18                 flag = 1;
19                 break;
20             }
21             a[i+1] -= a[i];
22         }
23         if(a[n-1] != 0) flag = 1;
24         if(flag)    printf("NO\n");
25         else        printf("YES\n");
26     }
27     return 0;
28 }
View Code

 

posted @ 2014-08-02 11:44  浙西贫农  阅读(236)  评论(0编辑  收藏  举报