LCP Array
Time Limit: 4000/2000 MS (Java/Others)
Memory Limit: 131072/131072 K (Java/Others)
问题描述
Peter有一个字符串s=s1s2...sn, 令suffi=sisi+1...sn是s第i字符开头的后缀. Peter知道任意两个相邻的后缀的最长公共前缀ai=lcp(suffi,suffi+1)(1≤i<n). 现在给你数组a, Peter有多少个仅包含小写字母的字符串满足这个数组. 答案也许会很大, 你只要输出对109+7取模的结果即可.
输入描述
输入包含多组数据. 第一行有一个整数T, 表示测试数据的组数. 对于每组数据: 第一行包含一个整数n (2≤n≤105)表示字符串的长度. 第二行包含n−1个整数: a1,a2,...,an−1 (0≤ai≤n). 所有数据中n的和不超过106.
输出描述
对于每组数据, 输出答案对109+7取模的结果.
输入样例
3 3 0 0 4 3 2 1 3 1 2
输出样例
16250 260
题解:
如果ai=x, 那么可以推断出si=si+1=...=si+x, 并且如果ai≠0, 那么ai+1=ai−1, 利用第二个条件判断无解, 利用第一个条件划分等价类. 假设有m个等价类, 那么答案就是26⋅25m−1
从后面往前面算要比较简单,而且不容易出错,实际上只需要统计0的个数就行,另一个条件是判断有无解的。
#include <stdio.h> #define LL long long const int maxn = 100005; int a[maxn]; LL ans, MOD = 1000000007; int main ( ) { int T, n; scanf ( "%d", &T ); while ( T -- ) { scanf ( "%d", &n ); for ( int i = 1; i < n; i ++ ) scanf ( "%d", &a[i] ); int mx = 0; ans = 26; for ( int i = n-1; i >= 1; i -- ) { if ( a[i] == 0 ) //统计零的个数就行了 { mx = 0; ans = ( ans*25 )%MOD; continue ; } if ( a[i] != mx+1 ) { ans = 0; break ; } else { mx = a[i]; } } printf ( "%I64d\n", ans ); } return 0; }
Shortest Path
Time Limit: 4000/2000 MS (Java/Others)Memory Limit: 131072/131072 K (Java/Others)此题可以直接建边,相邻两个节点和新添加的边,然后将从新加边出发到各点的最短距离算出,找一个最小的就可了。问题描述有一条长度为n的链. 节点i和i+1之间有长度为1的边. 现在又新加了3条边, 每条边长度都是1. 给出m个询问, 每次询问两点之间的最短路.输入描述输入包含多组数据. 第一行有一个整数T, 表示测试数据的组数. 对于每组数据: 第一行包含2个整数n和m (1≤n,m≤105)表示节点的数目和询问数目. 接下来一行包含6个有空格分开的整数a1,b1,a2,b2,a3,b3 (1≤a1,a2,a3,b1,b2,b3≤n), 表示新加的三条边为(a1,b1), (a2,b2), (a3,b3). 接下来m行, 每行包含两个整数si和ti (1≤si,ti≤n), 表示一组询问. 所有数据中m的和不超过106.输出描述对于每组数据, 输出一个整数S=(i=1∑mi⋅zi) mod (109+7), 其中zi表示第i组询问的答案.输入样例1 10 2 2 4 5 7 8 10 1 5 3 1输出样例7#include <stdio.h> #include <vector> #include <string.h> #include <queue> #include <algorithm> #define LL long long using namespace std; const int maxn = 100005, MOD = 1e9+7; int a[6], d[6][maxn]; bool inq[maxn]; vector < int > mp[maxn]; void init ( int n ) { for ( int i = 0; i <= n; i ++ ) mp[i].clear ( ); for ( int i = 1; i <= n; i ++ ) { if ( i > 1 ) //相邻建边 { mp[i].push_back ( i-1 ); mp[i-1].push_back ( i ); } } } void spfa ( int k, int s ) { memset ( d[k], 0x3f, sizeof ( d[k] ) ); memset ( inq, false, sizeof ( inq ) ); d[k][s] = 0; queue < int > q; q.push ( s ); while ( ! q.empty ( ) ) { int u = q.front ( ); q.pop ( ); inq[u] = false; for ( int i = 0; i < mp[u].size ( ); i ++ ) { int v = mp[u][i]; if ( d[k][v] > d[k][u]+1 ) { d[k][v] = d[k][u]+1; if ( inq[v] == false ) { inq[v] = true; q.push ( v ); } } } } } void solve ( ) { int T, n, m, u, v; scanf ( "%d", &T ); while ( T -- ) { scanf ( "%d%d", &n, &m ); init ( n ); for ( int i = 0; i < 3; i ++ ) { scanf ( "%d%d", &a[i*2], &a[i*2+1] ); mp[ a[i*2] ].push_back ( a[i*2+1] ); //新加的边建边 mp[ a[i*2+1] ].push_back ( a[i*2] ); } for ( int i = 0; i < 6; i ++ ) spfa ( i, a[i] ); //从a[i]这点出发到其他点的最短路 int ans = 0; for ( int i = 1; i <= m; i ++ ) { scanf ( "%d%d", &u, &v ); if ( u > v ) swap ( u, v ); int z = v-u; for ( int j = 0; j < 6; j ++ ) z = min ( z, d[j][u]+d[j][v] ); //从a[j]出发到达uv的最短路和 ans = ( ans+( LL )i*z )%MOD;//乘法数据超出范围 } printf ( "%d\n", ans ); } } int main ( ) { solve ( ); return 0; }
Transform
Time Limit: 4000/2000 MS (Java/Others)Memory Limit: 131072/131072 K (Java/Others)这道题因为是对位直接操作,是可以将s-t看成0-s^t就行了,因为s与t二进制为相同的地方可以不需要变化,问题描述给出n个整数, 对于一个整数x, 你可以做如下的操作若干次: + 令x的二进制表示为b31b30...b0, 你可以翻转其中一个位. + 令y是给出的其中一个整数, 你可以把x变为x⊕y, 其中⊕表示位运算里面的异或操作. 现在有若干整数对(S,T), 对于每对整数你需要找出从S变成T的最小操作次数.输入描述输入包含多组数据. 第一行有一个整数T (T≤20), 表示测试数据组数. 对于每组数据: 第一行包含两个整数n和m (1≤n≤15,1≤m≤105), 表示给出整数的数目和询问的数目. 接下来一行包含n个用空格分隔的整数a1,a2,...,an (1≤ai≤105). 接下来m行, 每行包含两个整数si和ti (1≤si,ti≤105), 代表一组询问.输出描述对于每组数据, 输出一个整数S=(i=1∑mi⋅zi) mod (109+7), 其中zi是第i次询问的答案.输入样例1 3 3 1 2 3 3 4 1 2 3 9输出样例10Hint3→4 (2次操作): 3→7→4 1→2 (1次操作): 1⊕3=2 3→9 (2次操作): 3→1→9然后预处理0-10^5需要的步数就可了。
#include <stdio.h> #include <string.h> #include <utility> #include <queue> #define LL long long using namespace std; typedef pair < int, LL > P; const int N = 15, maxn = 140005, MOD = 1e9+7; int a[N], ans[maxn], vis[maxn], n; void bfs ( int s ) //将0~2^17的变换次数全部算出来 { priority_queue < P, vector < P >, greater < P > > q; q.push ( P ( 0, s ) ); vis[s] = 1; ans[s] = 0; while ( ! q.empty ( ) ) { P p = q.top ( ); q.pop ( ); for ( int i = 0; i < n; i ++ ) //x^a[i] { LL x = p.second ^ a[i]; if ( x >= maxn || vis[x] ) continue ; vis[x] = 1; ans[x] = p.first+1; q.push ( P ( p.first+1, x ) ); } for ( int i = 0; i <= 17; i ++ ) //x变某一位 { LL x = p.second ^ ( 1 << i ); if ( x >= maxn || vis[x] ) continue ; vis[x] = 1; ans[x] = p.first+1; q.push ( P ( p.first+1, x ) ); } } } void solve ( ) { int T, m, s, t; LL res; scanf ( "%d", &T ); while ( T -- ) { memset ( ans, -1, sizeof ( ans ) ); memset ( vis, 0, sizeof ( vis ) ); scanf ( "%d%d", &n, &m ); for ( int i = 0; i < n; i ++ ) scanf ( "%d", &a[i] ); bfs ( 0 ); res = 0; for ( int i = 1; i <= m; i ++ ) { scanf ( "%d%d", &s, &t ); //因为s-t是进行位变换,所以相同位可以不用变,就相当于从0变换到s^t的步数 res = ( res+( LL )i*ans[s^t] )%MOD; } printf ( "%I64d\n", res ); } } int main ( ) { solve ( ); return 0; }
Toposort
Time Limit: 10000/5000 MS (Java/Others)Memory Limit: 131072/131072 K (Java/Others)问题描述给出n个点m条边的有向无环图. 要求删掉恰好k条边使得字典序最小的拓扑序列尽可能小.输入描述输入包含多组数据. 第一行有一个整数T, 表示测试数据组数. 对于每组数据: 第一行包含3个整数n, m和k (1≤n≤100000,0≤k≤m≤200000), 表示图中结点数目, 图中边的数目以及要删的边数. 接下来m行, 每行包含两个整数ui and vi, 表示存在一条ui到vi的有向边 (1≤ui,vi≤n). 输入保证给定的图是一个DAG. 输入数据中n的和不超过106. 输入数据中m的和不超过2⋅106.输出描述对于每组数据, 输出一个整数S=(i=1∑ni⋅pi) mod (109+7), 其中p1,p2,...,pn是字典序最小的那个拓扑序列.输入样例3 4 2 0 1 2 1 3 4 5 1 2 1 3 1 4 1 2 3 2 4 4 4 2 1 2 2 3 3 4 1 4输出样例30 27 30题解:
参考下普通的用堆维护求字典序最小拓扑序, 用某种数据结构维护入度小于等于k的所有点, 每次找出编号最小的, 并相应的减少k即可.
这个数据结构可以用线段树, 建立一个线段树每个节点[l,r]维护编号从l到r的所有节点的最小入度, 查询的时候只需要在线段树上二分, 找到最小的x满足入度小于等于k.
复杂度O((n+m)logn)
用优先队列维护入度小于等于K的节点,每次push最小的点,更新与其相连的入度,继续加入。用线段树也是一样的#include <stdio.h> #include <vector> #include <queue> #include <string.h> #include <utility> #define LL long long using namespace std; typedef pair < int, int > P; const int maxn = 100005, MOD = 1e9+7; vector < int > mp[maxn]; int d[maxn], vis[maxn]; void init ( int n ) { for ( int i = 0; i <= n; i ++ ) { mp[i].clear ( ); d[i] = 0; vis[i] = 0; } } void solve ( ) { int T, n, k, m, u, v; priority_queue < int, vector < int >, greater < int > > q; scanf ( "%d", &T ); while ( T -- ) { scanf ( "%d%d%d", &n, &m, &k ); init ( n ); while ( m -- ) { scanf ( "%d%d", &u, &v ); mp[u].push_back ( v ); d[v] ++; } for ( int i = 1; i <= n; i ++ ) //用优先队列维护入度小于等于k { if ( d[i] <= k ) q.push ( i ); } LL ans = 0; for ( int i = 1; i <= n; i ++ ) { int u; while ( true ) { u = q.top ( ); q.pop ( ); if ( vis[u] == 0 && d[u] <= k ) break ; } vis[u] = 1; //标记已用 k -= d[u]; //去掉连接u的边 ans = ( ans+( LL )i*u )%MOD; for ( int j = 0; j < mp[u].size ( ); j ++ ) { int v = mp[u][j]; if ( vis[v] == 0 ) { d[v] --; if ( d[v] <= k ) //小于等于k q.push ( v ); } } } printf ( "%I64d\n", ans ); } } int main ( ) { solve ( ); return 0; }
#include <stdio.h> #include <string.h> #define INF 0x3f3f3f3f #define lson l, m, rt << 1 #define rson m+1, r, rt << 1 | 1 #define LL long long const int maxn = 200005, N = 100005, MOD = 1e9+7; int head[maxn], nxt[maxn], to[maxn], pos; int min_rd[N << 2], ans[N], k, d[N]; void addEdge ( int u, int v ) { to[pos] = v; nxt[pos] = head[u]; head[u] = pos ++; } inline int Min ( int a, int b ) { return a < b ? a : b; } void PushUP ( int rt ) { min_rd[rt] = Min ( min_rd[rt << 1], min_rd[rt << 1 | 1] ); } void update ( int q, int v, int l, int r, int rt ) { if ( l == r ) { min_rd[rt] = v; return ; } int m = ( l+r ) >> 1; if ( q <= m ) update ( q, v, lson ); else update ( q, v, rson ); PushUP ( rt ); } int query ( int q, int l, int r, int rt ) { if ( l == r ) return l; int m = ( l+r ) >> 1, u; if ( min_rd[rt << 1] <= q ) u = query ( q, lson ); else u = query ( q, rson ); return u; } void solve ( ) { int T, n, m, u, v; scanf ( "%d", &T ); while ( T -- ) { pos = 0; memset ( head, -1, sizeof ( head ) ); memset ( d, 0, sizeof ( d ) ); scanf ( "%d%d%d", &n, &m, &k ); while ( m -- ) { scanf ( "%d%d", &u, &v ); addEdge ( u, v ); d[v] ++; } for ( int i = 1; i <= n; i ++ ) update ( i, d[i], 1, n, 1 );//直接修改,更新对数组更新 int cnt = 0; for ( int i = 1; i <= n; i ++ ) { int u = query ( k, 1, n, 1 ); ans[cnt ++] = u; k -= d[u]; //去掉d[u]条边 d[u] = INF; //设为∞ update ( u, d[u], 1, n, 1 ); for ( int j = head[u]; ~ j; j = nxt[j] ) { int v = to[j]; d[v] --; //入度-1 update ( v, d[v], 1, n, 1 ); } } /* for ( int i = 0; i < cnt; i ++ ) printf ( "%d ", ans[i] ); printf ( "\n" ); */ LL res = 0; for ( int i = 1; i <= n; i ++ ) res = ( res+( LL )i*ans[i-1] )%MOD; printf ( "%I64d\n", res ); } } int main ( ) { solve ( ); return 0; }