[后缀树分治][因式分解][三分]
因式分解
【问题描述】
给定正整数n ,你需要将多项式x^n -1 进行整数域上的因式分解。并且分解后的每一个每个因式应均为不可约多项式(即在整数域上不可再分)。对于分解出的所有因式,你需要对它们进行排序后再输出。排序方式如下:最高次项指数小的在前;如果一样,按指数从高到低比较各项的系数:先比较绝对值的大小,小的在前;再比较符号,负号在前。
【输入文件】
一行一个正整数n 。
【输出文件】
输出一行,为因式分解后的结果
fac.in
6
fac.out
(x-1)(x+1)(x^2-x+1)(x^2+x+1)
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
#define maxn 1010
int n;
struct opt{
int a[maxn], n;
opt(){memset(a, 0, sizeof a);}
bool operator<(const opt& k)const{
if(n != k.n)return n < k.n;
for(int i = n; ~i; i --){
if(a[i] != k.a[i]){
if(a[i] && k.a[i])return a[i] < k.a[i];
else return a[i] == 0;
}
}return true;
}
void print(){
if(n == 1){
if(a[n] == 1)printf("(x");
else if(a[n] == -1)printf("(-x");
else printf("(%dx", a[n]);
if(a[0] > 0)printf("+");
printf("%d)", a[0]);
return;
}
printf("(");
for(int i = n; i > 1; i --){
if(!a[i])continue;
if(i == n){
if(a[i] == 1)printf("x^%d", i);
else if(a[i] == -1)printf("-x^%d", i);
else printf("%dx^%d", a[i], i);
}
else{
if(a[i] > 0)printf("+");
if(a[i] == 1)printf("x^%d", i);
else if(a[i] == -1)printf("-x^%d", i);
else printf("%dx^%d", a[i], i);
}
}
if(a[1]){
if(a[1] == 1)printf("+x");
else if(a[1] == -1)printf("-x");
else{
if(a[1] > 0)printf("+");
printf("%dx", a[1]);
}
}
if(a[0] > 0)printf("+");
printf("%d", a[0]);
printf(")");
}
}f[maxn], ans[maxn];
opt operator / (opt a, const opt& b){
opt _;
_.n = a.n - b.n;
for(int i = a.n; i >= b.n; i --){
int k = a.a[i] / b.a[b.n];
for(int j = b.n; j >= 0; j --)
a.a[i - b.n + j] -= k * b.a[j];
_.a[i - b.n] = k;
}
return _;
}
int main(){
freopen("fac.in", "r", stdin);
freopen("fac.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i ++){
f[i].a[0] = -1;
f[i].a[i] = 1;
f[i].n = i;
}
for(int i = 1; i <= n; i ++)
for(int j = 1; j < i; j ++)
if(i % j == 0)f[i] = f[i] / f[j];
int cnt = 0;
for(int i = 1; i <= n; i ++)
if(n % i == 0)ans[++ cnt] = f[i];
sort(ans + 1, ans + 1 + cnt);
for(int i = 1; i <= cnt; i ++)
ans[i].print();
return 0;
}
任务
【问题描述】
n 个人正在完成m 件任务。
每个人都有一定的能力,我们可以用正整数ai 来描述每个人的能力;每件任务都有一定的难度,我们可以用正整数b j 来描述每个任务的难度。具体地,编号为i 的人完成编号为j 的任务所需要的时间为ai * bj 。现在,你需要让n 个人按编号依次完成m 件任务,即第一个人从第一件任务开始,一直完成到最后一件;当第一个人完成第一件任务时,第二个人开始进行第一个任务……在此过程中,你需要保证一个任务只能正在由一个人完成。
你需要求出最后一个人完成最后一件任务的最早时间。
【输入文件】
第一行两个正整数m, n 。
第二行m 个正整数,分别表示b1, b2 ,..., bm 。
第三行n 个正整数,分别表示a1 , a2 ,..., an 。
【输出文件】
输出一行一个正整数,表示最后一个人完成最后一件任务的最早时间。
【输入输出样例】
mission.in
3 4
1 2 3
3 4 1 2
mission.out
40
【数据规模和约定】
对于40% 的数据,满足n, m 《 1000 。
对于100% 的数据,满足1 《 n, m 《100000 ,1 《 ai , bj 《 10000 。
点积在凸壳上三分
详细见向量集。
#include <stdio.h>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
#define maxn 100010
int n, m, b[maxn], a[maxn];
ll s[maxn];
struct Point{
ll x, y;
bool operator<(const Point& k)const{if(x != k.x)return x < k.x;return y < k.y;}
Point operator-(const Point& b)const{return (Point){x - b.x, y - b.y};}
ll operator^(const Point& k)const{return x * k.y - y * k.x;}
ll operator*(const Point& k)const{return x * k.x + y * k.y;}
}p[maxn], st[maxn], Nw;
int top;
ll ask(ll x, ll y){
int l = 1, r = top;
Nw = (Point){x, y};
ll ret = 0;
while(r - l > 3){
int m1 = (l + l + r) / 3, m2 = (l + r + r) / 3;
if((Nw * st[m1]) < (Nw * st[m2]))l = m1;
else r = m2;
}
for(int i = l; i <= r; i ++)
ret = max(ret, Nw * st[i]);
return ret;
}
int main(){
freopen("mission.in", "r", stdin);
freopen("mission.out", "w", stdout);
scanf("%d%d", &m, &n);
for(int i = 1; i <= m; i ++)scanf("%d", &b[i]);
for(int i = 1; i <= n; i ++)scanf("%d", &a[i]);
for(int i = 1; i <= m; i ++){
s[i] = s[i-1] + b[i];
p[i] = (Point){s[i], -s[i-1]};
}
sort(p + 1, p + m + 1);
st[++ top] = p[1];
if(m > 1)st[++ top] = p[2];
for(int i = 3; i <= m; i ++){
while(top > 1 && ((st[top] - st[top-1]) ^ (p[i] - st[top-1])) >= 0)
top --;
st[++ top] = p[i];
}
ll f = 0;
for(int i = 2; i <= n; i ++)
f += ask(a[i-1], a[i]);
printf("%lld\n", f + s[m] * a[n]);
return 0;
}
往事
【问题描述】
往事太多,有时候忘了就忘了吧。如果有非记不可的,就只能用点附加手段啦!我们定义一棵往事树是一个n 个点n-1 条边的有向无环图,点编号为1 到n ,其中1 号点被称为是根结点,除根结点以外,每个点都恰有一条出边(即以其作为起点的边)。每条边上有1 个字符(这里我们实际上用一个不大于300 的非负整数代表不同的字符),对于任意一个点u , u 的所有入边(即以其为终点的边)上的字符互不相同。
接下来我们定义往事,点u 对应的往事为u 到根的路径(显然有且只有一条)上的所有边按照从u 到根经过的顺序排序后边上的字符依次拼接形成的字符串,简记为r(u) 。
一棵往事树的联系度取决于它包含的所有往事之中最相近的一对的相似度。具体的,我们定义2 个点u 和点v 对应的往事的相似度f (u, v) 如下:
f (u, v) = Lcp(r(u), r(v)) + Lcs(r(u), r(v)) 。其中Lcp(a, b) 表示字符串a 和b 的最长公共前缀的长度, Lcs(a, b) 表示字符串a 和b 的最长公共后缀的长度。
定义一棵往事树的联系度为所有满足1 <= u < v <= n 的f (u, v) 的最大值。
现在,给出一棵往事树,请你给出这棵往事树的联系度。
【输入文件】
第一行一个大于1 的整数n 表示给定的往事树的结点数。
接下来n-1 行,每行2 个整数。其中第i 行的2 个数依次表示点i +1 的
出边的终点和边上的字符。
【输出文件】
一行一个整数表示这棵往事树的联系度。
用树分治求出两两黑点的LCA的深度+parent树上的最大值。
注意分治统计的时候不要忘记点u。
注意分治的时候先标记上u。
Input
7
1 1
1 2
2 1
2 2
5 1
5 2
Output
3
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <map>
using namespace std;
#define maxn 400010
int n, ans, mp[maxn];
//------------------------------------------------//
namespace BIT{
int vis[maxn], t[maxn], n, tim;
#define lowbit(i) i & (~i + 1)
void init(){tim ++;}
void update(int pos, int val){
for(int i = pos; i <= n; i += lowbit(i))
if(vis[i] == tim)t[i] += val;
else vis[i] = tim, t[i] = val;
}
int ask(int pos){
int ret = 0;
for(int i = pos; i; i -= lowbit(i))
if(vis[i] == tim)ret += t[i];
return ret;
}
}
namespace Sam{
struct Node{map<int, int> nxt; int len, link;}st[maxn];
int root, Size;
void init(){
root = Size = 0;
st[root].len = 0;
st[root].link = -1;
}
struct Edge{int to, nxt;}edge[maxn];
int h[maxn], cnt;
void add(int u, int v){
edge[++ cnt] = (Edge){v, h[u]};
h[u] = cnt;
}
int dfs_clock, In[maxn], Out[maxn], anc[maxn][20], dep[maxn];
void Extend(int p, int Id, int c){
int cur = ++ Size;
st[cur].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == 0; p = st[p].link)
st[p].nxt[c] = cur;
if(p == -1)
st[cur].link = root;
else{
int q = st[p].nxt[c];
if(st[q].len == st[p].len + 1)
st[cur].link = q;
else{
int clone = ++ Size;
st[clone] = st[q];
st[clone].len = st[p].len + 1;
for(; ~p && st[p].nxt[c] == q; p = st[p].link)
st[p].nxt[c] = clone;
st[q].link = st[cur].link = clone;
}
}
mp[Id] = cur;
}
void dfs(int u){
In[u] = ++ dfs_clock;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
anc[v][0] = u, dep[v] = dep[u] + 1;
dfs(v);
}
Out[u] = dfs_clock;
}
void prepare(){
for(int i = 1; i <= Size; i ++)
add(st[i].link, i);
dfs(root);
BIT::n = dfs_clock;
for(int j = 1; 1 << j <= Size; j ++)
for(int i = 1; i <= Size; i ++)
anc[i][j] = anc[anc[i][j-1]][j-1];
}
bool check(int u){
return BIT::ask(Out[u]) - BIT::ask(In[u] - 1) > 0;
}
int getans(int v){
if(check(v))
return st[v].len;
for(int i = 18; i >= 0; i --)
if(anc[v][i] && !check(anc[v][i]))
v = anc[v][i];
return st[anc[v][0]].len;
}
}
namespace Divide_and_Conquer{
struct Edge{int to, nxt;}edge[maxn];
int h[maxn], cnt;
void add(int u, int v){
edge[++ cnt] = (Edge){v, h[u]}; h[u] = cnt;
edge[++ cnt] = (Edge){u, h[v]}; h[v] = cnt;
}
int g, Mx, top, stack[maxn], size[maxn], dep[maxn], anc[maxn][20];
bool vis[maxn];
int askLCA(int p, int q){
if(dep[p] < dep[q])swap(p, q); int log = 1;
for(; 1 << log <= dep[p]; log ++); log --;
for(int i = log; ~i; i --)
if(anc[p][i] && dep[anc[p][i]] >= dep[q])
p = anc[p][i];
if(p == q)return p;
for(int i = log; ~i; i --)
if(anc[p][i] && anc[p][i] != anc[q][i])
p = anc[p][i], q = anc[q][i];
return anc[p][0];
}
void dfs(int u, int fa){
anc[u][0] = fa;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v < u)continue;
dep[v] = dep[u] + 1;
dfs(v, u);
}
}
void getg(int u, int fa){
size[u] = 1; int f = 0;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(v == fa || vis[v])continue;
getg(v, u), size[u] += size[v];
f = max(f, size[v]);
}
f = max(f, Mx - size[u]);
if(f * 2 <= Mx)g = u;
}
void DFS(int u, int fa){
stack[++ top] = u;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(vis[v] || v == fa)continue;
DFS(v, u);
}
}
void divide(int u){
vis[u] = true;
BIT::init();
BIT::update(Sam::In[mp[u]], 1);
int fa = 0;
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(vis[v])continue;
if(v < u){fa = v;continue;}
top = 0;
DFS(v, u);
for(int j = 1; j <= top; j ++)
ans = max(ans, Sam::getans(mp[stack[j]]) + dep[u]);
for(int j = 1; j <= top; j ++)
BIT::update(Sam::In[mp[stack[j]]], 1);
}
if(fa != 0){
top = 0;
DFS(fa, u);
for(int j = 1; j <= top; j ++)
ans = max(ans, Sam::getans(mp[stack[j]]) + dep[askLCA(stack[j], fa)]);
}
for(int i = h[u]; i; i = edge[i].nxt){
int v = edge[i].to;
if(vis[v])continue;
Mx = size[v];
g = v;
getg(v, 0);
divide(g);
}
}
void solve(){
dfs(1, 0);
Mx = n;
for(int j = 1; 1 << j <= n; j ++)
for(int i = 1; i <= n; i ++)
anc[i][j] = anc[anc[i][j-1]][j-1];
getg(1, 0);
divide(g);
}
}
int main(){
freopen("recall.in", "r", stdin);
freopen("recall.out", "w", stdout);
int __size__ = 128 << 20;
char *__p__ = (char*)malloc(__size__) + __size__;
__asm__("movl %0, %%esp\n" :: "r"(__p__));
Sam::init();
scanf("%d", &n);
int f, c;
for(int u = 2; u <= n; u ++){
scanf("%d%d", &f, &c);
Sam::Extend(mp[f], u, c);
Divide_and_Conquer::add(f, u);
}
Sam::prepare();
Divide_and_Conquer::solve();
printf("%d\n", ans);
return 0;
}

浙公网安备 33010602011771号