首页 > 代码库 > CSU-ACM暑假集训基础组七夕专场

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