7月22-集训个人赛第三场
2013-07-22 09:38 bootstar 阅读(210) 评论(0) 编辑 收藏 举报A题,简单图论题: Codeforces 295B floyd变种
题意:对于一个带权有向图,每次删除一个点,要求计算输出删除该点前当前图的所有点间的最短路径之和。
解法:对于删除顺序,我们可以反过来做,这样就相当于每次添加一个点到图中,然后询问当前图的所有点间的最短路径之和。对于每次添加操作,就相当于用当前添加的点v,去更新整个图的最短路,也就是dist[s][t] = min(dist[s][t], dist[s][v]+dist[v][t]);
对于统计操作,直接暴力枚举即可。整个复杂度为O(n^3),唯一需要注意的就是,本题数据范围,需要用long long 来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <stdio.h> #include <string.h> #define maxn 505 typedef long long LL; bool mark[maxn]; LL dist[maxn][maxn]; LL seq[maxn]; LL ans[maxn]; LL min(LL x, LL y){ return x < y ? x : y; } void update( int s, int n){ for ( int v = 1; v <= n; v ++){ for ( int u = 1; u <= n; u ++){ dist[v][u] = min(dist[v][u], dist[v][s] + dist[s][u]); } } } LL query( int s, int n){ LL ret = 0; for ( int i = n, v = seq[i]; i >= s; v = seq[--i]){ for ( int j = n, u = seq[j]; j >= s; u = seq[--j]){ ret += dist[v][u]; } } return ret; } int main(){ for ( int n; scanf ( "%d" , &n)!=EOF; ){ for ( int i = 1; i <= n; i ++){ for ( int j = 1; j <= n; j ++) scanf ( "%I64d" , &dist[i][j]); mark[i] = false ; } for ( int i = 1;i <= n; i ++) scanf ( "%d" , &seq[i]); for ( int i = n; i >= 1; i --){ mark[seq[i]] = 1; update(seq[i], n); ans[i] = query(i, n); } for ( int i = 1; i <= n; i ++){ printf ( "%I64d " , ans[i]); } printf ( "\n" ); } return 0; } |
B题,简单数学题: Codeforces 154B
题意:有n个人,m个操作。每个人有一个编号i,每个人有两种行为,行为+ i,表示编号为i的人需要去做实验,此时需要满足当前正在工作的人(设编号为j)有gcd(i, j) = 1;行为- i表示编号为i的人退出实验。对于每个操作需要判断是否成功。
解法:操作中最麻烦的就是怎么样查找谁与谁冲突。考虑i,j如果冲突,也就是说gcd(i, j)!=1,那么我们可以对gcd(i, j)分解为若干个素数的乘积,设为pi,显然存在且只存在一个j,使得pi可以整除gcd(i, j)。(否则设存在k与i冲突且pi整除gcd(k, i),那么k肯定与j冲突。)
处理:首先预处理出10^5内的素数,然后维护一个数组用于表示对应素数的倍数,初始化为0。然后对于+操作,进行素数分解,然后对于每个素数进行查询倍数,如果不是0,就出现了冲突。对于- 操作,同样进行素数分解,并将对应的素数的倍数赋值为0.对于已操作过,这个可以直接利用一个数组进行标记。整个复杂度为O(nlogn + nsqrt(n)).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include <stdio.h> #include <string.h> #define maxn 100000 bool f[maxn+5]; int pos[maxn]; int p[maxn/10], g[maxn/10]; int total; void process( int n){ memset (f, 0, sizeof (f)); total = 0; for ( int i = 2; i <= n; i ++){ if (!f[i]){ pos[i] = total; p[total ++] = i; for ( int j = i; j <= n; j += i){ f[j] = 1; } } } memset (f, 0, sizeof (f)); memset (g, 0, sizeof (g)); } int solve( int n){ for ( int i = 2; i*i <= n; i ++){ if (n%i==0){ if (g[pos[i]]) return g[pos[i]]; while (n%i==0) n/=i; } } if (n!=1 && g[pos[n]]) return g[pos[n]]; return 0; } int refresh( int n, int x){ for ( int i = 2; i*i<=n; i ++){ if (n%i==0){ g[pos[i]] = x; while (n%i==0) n/=i; } } if (n!=1) g[pos[n]] = x; return 0; } int main(){ int n, m, x; char cmd[3]; scanf ( "%d%d" , &n, &m); process(n); for ( int i = 0; i < m; i ++){ scanf ( "%s%d" , cmd, &x); if (cmd[0]== '+' ){ if (f[x]){ printf ( "Already on\n" ); continue ; } int t = solve(x); if (t) printf ( "Conflict with %d\n" , t); else { f[x] = 1; refresh(x, x); printf ( "Success\n" ); } } else { if (!f[x]) printf ( "Already off\n" ); else f[x] = 0, refresh(x, 0), printf ( "Success\n" ); } } return 0; } |
C题,简单数学题 Codefoces 150B
题意:对于一个大小为M的字符集,求长度为n的字符串的个数,要求字符串满足对于所有长度为k的连续子串为回文串。
解法:由于要求长度为k的字符串必须为回文串。那么我们可以先预处理一下哪些位置必须放同样的字符(建图操作)。这样我们就可以将整个位置分成x个集合(直接染色),同一个集合内的位置能放的字符必定是一样的。对于每个集合能放的字符的种类是一样的,所以就是m^x.整个算法的复杂度为O((n-k)*k/2 + n + log m)
1 #include <stdio.h> 2 #include <string.h> 3 #define maxn 2005 4 typedef long long LL; 5 const LL mod = 1000000007; 6 7 int g[maxn][maxn]; 8 int col[maxn], cnt; 9 10 LL quick_power(LL a, LL b){ 11 LL r = 1; 12 for(;b;b>>=1){ 13 if(b&1) r = a * r %mod; 14 a = a * a %mod; 15 } 16 return r; 17 } 18 void dfs(int v, int f, int c, int n){ 19 col[v] = c; 20 for(int i = 1; i <= n; i ++){ 21 if(!g[v][i]|| i == f || col[i]) continue; 22 dfs(i, v, c, n); 23 } 24 } 25 int main(){ 26 int n, m, k; 27 scanf("%d%d%d", &n, &m, &k); 28 if(k > n){ 29 printf("%I64d\n", quick_power(m, n)); 30 return 0; 31 } 32 for(int i = 1; i + k - 1<= n; i ++){ 33 for(int a = i, b = i + k - 1; a < b; a ++ , b --){ 34 g[a][b] = g[b][a] = 1; 35 } 36 } 37 for(int i = 1; i <= n; i ++){ 38 if(!col[i]){ 39 cnt ++; 40 dfs(i, -1, cnt, n); 41 } 42 } 43 printf("%I64d\n", quick_power(m, cnt)); 44 return 0; 45 }
D题,数据结构:线段树 Codefoces 266E
题意:这里就不在描述了,题目很短= =
解法:这个题和杭州邀请赛的C题是同一类型的。主要是对于
的计算。0<=k<=5,可以考虑直接展开(i-l+1)^k:
k = 0: 1
k = 1: i - l + 1
k = 2: i^2 - 2(l-1)i + (l-1)^2
k = 3: i^3 - 3i^2(l-1) + 3i(l-1)^2 - (l-1)^3
k = 4: i^4 - 4i^3(l-1) + 6i^2(l-1)^2 - 4i(l-1)^3 + (l-1)^4
k = 5: i^5 - 5i^4(l-1) + 10i^3(l-1)^2 - 10i^2(l-1)^3 + 5i(l-1)^4 - (l-1)^5
那么由于l-1项只与l有关,我们只需要用线段树维护:i^5ai , i^4ai, i^3ai, i^2ai, iai, ai的区间和。然后剩余的就是线段树的工作了。对于查询操作,可以直接将区间的上述和直接计算到,然后可以用按照二项式展开统一计算。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #include <stdio.h> #include <string.h> #define maxn 100005 #define lson(x) (x<<1) #define rson(x) (x<<1|1) #define md(x, y) ((x+y)>>1) typedef long long LL; #define MOD 1000000007 LL val[6][maxn]; LL ans[6]; int C[6][6]; struct Tree{ LL f[6][maxn*4]; LL v[maxn*4]; void clear(){ memset (f, 0, sizeof (f)); memset (v, 0, sizeof (v)); } void build( int c, int left, int right){ if (left == right){ scanf ( "%I64d" , &v[c]); LL t = 1; for ( int i = 0; i <= 5; i ++){ f[i][c] = t * v[c]%MOD; t = t * left %MOD; } return ; } int m = md(left, right); build(lson(c), left, m); build(rson(c), m + 1, right); push_up(c); } void push_down( int c, int left, int right){ if (v[c] == -1) return ; int l = lson(c), r = rson(c); v[l] = v[r] = v[c]; int m = md(left, right); for ( int i = 0; i <= 5; i ++){ f[i][l] = v[l] * (((val[i][m] - val[i][left-1])%MOD + MOD)%MOD)%MOD; f[i][r] = v[r] * (((val[i][right] - val[i][m])%MOD + MOD)%MOD)%MOD; f[i][c] = 0; } v[c] = -1; } void push_up( int c){ int l = lson(c), r = rson(c); for ( int i = 0; i <= 5; i ++){ f[i][c] = (f[i][l] + f[i][r])%MOD; } v[c] = -1; } void update( int c, int l, int r, int left, int right, LL x){ if (l <= left && right <= r){ v[c] = x; for ( int i = 0; i <= 5; i ++){ f[i][c] = x*(((val[i][right] - val[i][left-1])%MOD + MOD)%MOD)%MOD; } return ; } push_down(c, left, right); int m = md(left, right); if (r <= m) update(lson(c), l, r, left, m, x); else if (l > m) update(rson(c), l, r, m + 1, right, x); else { update(lson(c), l, m, left, m, x); update(rson(c), m + 1, r, m + 1, right, x); } push_up(c); } void query( int c, int l, int r, int left, int right, LL ans[]){ if (l <= left && right <= r){ for ( int i = 0; i <= 5; i ++){ ans[i] = (f[i][c] + ans[i])%MOD; } return ; } push_down(c, left,right); int m = md(left, right); if (r <= m) query(lson(c), l, r, left, m, ans); else if (l > m) query(rson(c), l, r, m+1, right, ans); else { query(lson(c), l, m, left, m, ans); query(rson(c), m + 1, r, m + 1, right, ans); } push_up(c); } }; Tree tree; void init( int n){ for ( int i = 1; i <= n; i ++){ LL t = 1; for ( int j = 0; j <= 5; j ++){ val[j][i] = (val[j][i-1] + t)%MOD; t = (t * i)%MOD; } } C[0][0] = 1; C[1][1] = C[1][0] = 1; for ( int i = 2; i <= 5; i ++){ C[i][0] = 1; for ( int j = 1; j <= i; j ++){ C[i][j] = C[i-1][j] + C[i-1][j-1]; } } } int main(){ int n, m, l, r, x; char cmd[2]; LL t; scanf ( "%d%d" , &n, &m); getchar (); init(n); tree.build(1, 1, n); for (;m --;){ scanf ( "%s%d%d%d" , cmd, &l, &r, &x); if (cmd[0] == '?' ){ memset (ans, 0, sizeof (ans)); tree.query(1, l, r, 1, n, ans); LL result = 0; t = 1; for ( int i = x, j = 0; i >=0; i --){ if (j&1) result = (result - ((ans[i]*t)%MOD)*C[x][j])%MOD; else result = (result + ((ans[i]*t)%MOD)*C[x][j])%MOD; t = (t * (l-1))%MOD; j ++; } printf ( "%I64d\n" , (result+MOD)%MOD); } else tree.update(1, l, r, 1, n, x); } return 0; } |
E题:DP题,一个简单的树形DP, Codefoces 161D
题意:题意比较简单,不在做描述了.
解法:简单树形DP。
答案分成两部分:
1.a, b两个点的距离为k,且,a为b的祖先
2.a, b两个点的距离为k,a,b分别在两颗不同的子树上。
dp[i][k]表示,以i为根的子树上,与i的距离为k的节点个数。
那么显然:dp[i][k] = sum{dp[s][k-1]}其中s是i的孩子节点。
对于第一部分的答案,显然就是dp[i][k]
对于第二部分的答案,是sum{dp[i][a]*dp[s][b]}其中s是当前待处理的孩子节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #include <stdio.h> #include <string.h> #include <vector> using namespace std; #define maxn 50005 vector< int > vec[maxn]; int dp[maxn][505]; long long ans; void dfs( int v, int f, int k){ for ( int i = 0; i <= k; i ++) dp[v][i] = 0; dp[v][0] = 1; for ( int i = 0; i < vec[v].size(); i ++){ int u = vec[v][i]; if (u==f) continue ; dfs(u, v, k); for ( int j = 0; j < k; j ++){ ans += dp[u][j] * dp[v][k - j - 1]; } for ( int j = 1; j < k; j ++){ dp[v][j] += dp[u][j-1]; } } ans += dp[v][k]; } int main(){ for ( int n, k; scanf ( "%d%d" , &n, &k)!=EOF; ){ for ( int i = 1; i <= n; i ++) vec[i].clear(); for ( int i = 1, x, y; i < n; i ++){ scanf ( "%d%d" , &x, &y); vec[x].push_back(y); vec[y].push_back(x); } ans = 0; dfs(1, 0, k); printf ( "%I64d\n" , ans); } return 0; } |
F题,比较有意思的字符串题,这个是智商题,表示智商拙计。Codeforces 224D
题意:给定两个串S和T,求是不是对于所有的Si,至少存在一个子串sub, sub是S的子串(不一定连续),Si属于sub,且sub==T.
解法:对于S串的每个位置i,求出以i为终点的S1...Si与T1...Tt的最长公共子序列的长度l[i],
然后求出以i为终点的Ss..Si+1Si与Tt...T1的最长公共子序列的长度r[i]。
然后直接判断向左匹配的最大长度l[i]与向右匹配的最大长度r[i],是不是有l[i]+r[i]-1 >= t。
由于只要求解长度,我们对于每个字符可以记录一个最长匹配长度,这样就可以做到O(n)的预处理匹配长度,然后可以做到O(n)的判断。
1 #include <stdio.h> 2 #include <string.h> 3 #define maxn 200005 4 5 char s[maxn], t[maxn]; 6 int l[maxn], r[maxn], pos[26]; 7 8 int main(){ 9 scanf("%s%s", s + 1, t + 1); 10 int ls = strlen(s + 1), lt = strlen(t + 1); 11 12 memset(pos, -1, sizeof(pos)); 13 for(int i = 1, j = 1; s[i]; i ++){ 14 if(t[j] && s[i]==t[j]){ 15 pos[s[i]-'a'] = j++; 16 } 17 l[i] = pos[s[i] - 'a']; 18 } 19 memset(pos, -1, sizeof(pos)); 20 for(int i = ls, j = 1; i > 0; i --){ 21 if(j <= lt && s[i]==t[lt - j + 1]){ 22 pos[s[i]-'a'] = j ++; 23 } 24 r[i] = pos[s[i]-'a']; 25 } 26 bool f = true; 27 for(int i = 1; s[i]; i ++){ 28 if(l[i] == -1 || r[i] == -1 || l[i] + r[i] < lt + 1){ 29 f = false; 30 break; 31 } 32 } 33 puts(f? "Yes" : "No"); 34 return 0; 35 }
【推荐】还在用 ECharts 开发大屏?试试这款永久免费的开源 BI 工具!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步