[LibreOJ 2058][TJOI2016]求和
求\(\sum_{i = 0}^n\sum_{j = 0}^i \begin{Bmatrix}i\\ j\end{Bmatrix}2^jj!\)。
\(1\leq n\leq 10^5\)。
[LibreOJ 2100][TJOI2015]线性代数
颓一波式子可以发现答案就是\(ABA^T-CA^T\)。然后发现对于一个\(B\)中的元素\(B_{i, j}\)要对答案做出贡献要求\(A_i\)和\(A_j\)都为1,而\(A_i\)为1会导致答案减去一个\(C_i\)。
因此我们可以分别对\(B\)中元素和\(A\)中元素建点。\(B\)中元素会带来收益,但是要求依赖两个\(A\)中元素。而\(A\)中元素会带来一定的笋丝。这个就已经事很明显的最大权闭合子图力,,,
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <functional>
#include <utility>
#include <vector>
#include <queue>
#include <stack>
const int maxn = 505;
const int maxno = 500 * 500 + 500 + 5;
const int maxm = 2 * (500 * 500 * 3 + 500 + 5);
int first[maxno];
int next[maxm], to[maxm], flow[maxm], cap[maxm];
int gcnt = 0;
void add_edge(int u, int v, int c) {
gcnt ++;
next[gcnt] = first[u]; first[u] = gcnt;
to[gcnt] = v; cap[gcnt] = c; flow[gcnt] = 0;
}
void ins_edge(int u, int v, int c) {
add_edge(u, v, c); add_edge(v, u, 0);
}
int rev(int i) {
if(1 & i) {
return i + 1;
} else {
return i - 1;
}
}
int d[maxno];
int s, t, num;
bool bfs() {
static bool vis[maxno];
std::fill(vis, vis + num + 1, false);
std::fill(d, d + num + 1, 0);
std::queue<int> Q; Q.push(s);
d[s] = 1; vis[s] = true;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
for(int i = first[u]; i; i = next[i]) {
int v = to[i];
if(!vis[v] && cap[i] > flow[i]) {
d[v] = d[u] + 1;
Q.push(v); vis[v] = true;
}
}
}
return vis[t];
}
int cur[maxno];
int dfs(int x, int a) {
if(a == 0 || x == t) return a;
int ret = 0;
for(int &i = cur[x]; i; i = next[i]) {
int v = to[i], f;
if(d[v] == d[x] + 1 && (f = dfs(v, std::min(a, cap[i] - flow[i]))) > 0) {
ret += f; a -= f;
flow[i] += f; flow[rev(i)] -= f;
if(a == 0) break;
}
}
if(a > 0) d[x] = -1;
return ret;
}
int dinic() {
int ret = 0;
while(bfs()) {
for(int i = 0; i <= num; i ++) cur[i] = first[i];
ret += dfs(s, 0x7f7f7f7f);
}
return ret;
}
int n;
int main() {
int ans = 0;
scanf("%d", &n);
s = 0, t = n * n + n + 1;
num = t;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= n; j ++) {
int u = (i - 1) * n + j; int val; scanf("%d", &val);
ans += val; ins_edge(s, u, val);
ins_edge(u, n * n + i, 0x7f7f7f7f);
ins_edge(u, n * n + j, 0x7f7f7f7f);
}
}
for(int i = 1; i <= n; i ++) {
int val; scanf("%d", &val);
ins_edge(n * n + i, t, val);
}
ans -= dinic();
printf("%d\n", ans);
return 0;
}
[LibreOJ 2102][TJOI2015]弦论
xjbYY了一下竟然就艹过去了……
如果说是我们知道了每个点出发能得到的串有多少个,我们就能用类似树上求\(k\)大的求法来搞了。问题来了,怎么知道这个?
我先说一下不重复的情况吧,首先到达这个点就有一种串了,然后我们加上他的出边(注意是转移边!)指向的点的值就行了。重复的情况我们考虑一下\(|Right(x)|\)就能搞出来了
代码:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <utility>
#include <queue>
#include <algorithm>
#include <set>
const int maxn = 500005;
const int maxno = maxn * 4;
const int bufsiz = 50 * 1024 * 1024;
typedef long long ll;
int fa[maxno], ch[maxno][26];
int len[maxno];
ll ans[maxno][2], val[maxno];
int rt, last;
int cur;
inline void init_sam() {
rt = last = 1;
cur = 1;
ans[1][0] = ans[1][1] = -1LL;
}
inline int alloc_node(int l = 0, int f = 0) {
int ret = ++ cur;
len[ret] = l; fa[ret] = f;
ans[ret][0] = ans[ret][1] = -1LL;
return ret;
}
inline int idx(char c) {
return c - 'a';
}
inline void extend(char cx) {
int c = idx(cx);
int np = alloc_node(len[last] + 1);
val[np] = 1LL;
int p = last;
while(p && !(ch[p][c])) ch[p][c] = np, p = fa[p];
if(!p) {
fa[np] = rt;
} else {
int q = ch[p][c];
if(len[q] == len[p] + 1) {
fa[np] = q;
} else {
int nq = alloc_node(len[p] + 1, fa[q]);
memcpy(ch[nq], ch[q], sizeof(ch[q]));
fa[q] = fa[np] = nq;
while(p && ch[p][c] == q) ch[p][c] = nq, p = fa[p];
}
}
last = np;
}
int lc[maxno], rb[maxno];
inline void add_edge(int c, int f) {
rb[c] = lc[f];
lc[f] = c;
}
void dfs_1(int x) {
for(int i = lc[x]; i; i = rb[i]) {
dfs_1(i);
val[x] += val[i];
}
}
void dfs_2(int x) {
if(ans[x][0] != -1LL) return;
ans[x][0] = 1LL; ans[x][1] = val[x];
for(int c = 0; c < 26; c ++) {
int v = ch[x][c];
if(v) {
dfs_2(v);
ans[x][0] += ans[v][0];
ans[x][1] += ans[v][1];
}
}
}
inline void process() {
for(int i = 2; i <= cur; i ++) {
add_edge(i, fa[i]);
}
dfs_1(1); dfs_2(1);
#ifdef LOCAL
for(int i = 1; i <= cur; i ++) {
printf("ans[%d] : (%lld, %lld)\n", i, ans[i][0], ans[i][1]);
}
#endif
}
char ret[maxno];
void search(ll k, int typ) {
static ll val_0[maxno];
for(int i = 1; i <= cur; i ++) {
val_0[i] = 1;
}
ll *self[2] = {val_0, val};
k += self[typ][1];
if(k > ans[1][typ]) {
ret[0] = '-', ret[1] = '1';
return;
}
int cnt = 0;
int u = 1;
while(k > self[typ][u]) {
int used = 0;
k -= self[typ][u];
for(int c = 0; c < 26; c ++) {
int v = ch[u][c];
if(!v) continue;
used ++;
if(k > ans[v][typ]) {
k -= ans[v][typ];
} else {
ret[cnt ++] = c + 'a';
#ifdef LOCAL
printf("Towardsing %d with %c\n", v, c + 'a');
printf("k : %lld\n", k);
#endif
u = v; break;
}
}
// if(used == 0) break;
}
}
int main() {
static char S[maxn];
scanf("%s", S); int n = strlen(S);
int typ; ll k; scanf("%d%lld", &typ, &k);
init_sam();
for(int i = 0; i < n; i ++) {
extend(S[i]);
}
process();
search(k, typ);
puts(ret);
return 0;
}
[BZOJ 3171][TJOI2013]循环格
流量平衡入门中……
我竟然想民白了……
这个题就是要求每个点在且仅在一个环中,这样每个点的入、出度都是1。出度肯定是1,接下来我们想想怎么保证入度为1。
我们建两排点(也可以说是拆点?),一排表示流出,一排表示接受。然后流出点向相邻的接收点连边,费用的话考虑是否和原来这一格的方向不一样就行了。
这个不需要判断是否满流啥的……因为一定有解(比如说每个行构成一个环啥的)。
代码:
/**************************************************************
Problem: 3171
User: danihao123
Language: C++
Result: Accepted
Time:28 ms
Memory:13844 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <utility>
#include <queue>
const int maxn = 20;
typedef long long ll;
char S[maxn][maxn];
int tot = 0; int num[maxn][maxn][2];
int R, C;
char D[4] = {'L', 'R', 'U', 'D'};
int tow(int i, int j, char dir, int flag) {
int dx = 0, dy = 0;
if(dir == 'L') {
dx = -1;
} else if(dir == 'R') {
dx = 1;
} else if(dir == 'U') {
dy = -1;
} else if(dir == 'D') {
dy = 1;
}
int nx = (i + dy + R) % R;
int ny = (j + dx + C) % C;
return num[nx][ny][flag];
}
const int maxno = 100005;
const int maxe = 400005;
int first[maxno];
int next[maxe], from[maxe], to[maxe], flow[maxe], cap[maxe];
ll cost[maxe];
int gcnt = 0;
void add_edge(int u, int v, int f, ll c) {
gcnt ++;
next[gcnt] = first[u];
first[u] = gcnt;
from[gcnt] = u; to[gcnt] = v;
cap[gcnt] = f; flow[gcnt] = 0;
cost[gcnt] = c;
}
int rev(int i) {
return ((i - 1) ^ 1) + 1;
}
void ins_edge(int u, int v, int f, ll c = 0LL) {
#ifdef LOCAL
printf("Inserting Edge (%d, %d, %d, %lld)\n", u, v, f, c);
#endif
add_edge(u, v, f, c);
add_edge(v, u, 0, -c);
}
const ll LINF = 0x7f7f7f7f7f7f7f7fLL;
bool spfa(int s, int t, int &f, ll &c) {
static ll d[maxno];
static bool inq[maxno];
static int a[maxno], p[maxno];
std::fill(d, d + tot + 1, LINF);
std::fill(inq, inq + tot + 1, false);
std::fill(a, a + tot + 1, 0);
std::fill(p, p + tot + 1, 0);
d[s] = 0;
std::queue<int> Q; Q.push(s); inq[s] = true;
a[s] = 0x7fffffff;
while(!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for(int i = first[u]; i; i = next[i]) {
if(cap[i] > flow[i]) {
int v = to[i];
if(d[v] > d[u] + cost[i]) {
d[v] = d[u] + cost[i];
a[v] = std::min(a[u], cap[i] - flow[i]); p[v] = i;
if(!inq[v]) {
Q.push(v); inq[v] = true;
}
}
}
}
}
if(a[t] == 0) return false;
f += a[t];
c += (ll(a[t])) * d[t];
for(int e = p[t]; e; e = p[from[e]]) {
flow[e] += a[t];
flow[rev(e)] -= a[t];
}
return true;
}
void mcmf(int s, int t, int &f, ll &c) {
while(spfa(s, t, f, c));
}
int main() {
tot = 1; int s = 0, t = 1;
scanf("%d%d", &R, &C);
for(int i = 0; i < R; i ++) {
scanf("%s", S[i]);
for(int j = 0; j < C; j ++) {
num[i][j][0] = ++ tot;
num[i][j][1] = ++ tot;
}
}
for(int i = 0; i < R; i ++) {
for(int j = 0; j < C; j ++) {
ins_edge(s, num[i][j][0], 1);
ins_edge(num[i][j][1], t, 1);
for(int it = 0; it < 4; it ++) {
int u = num[i][j][0];
int v = tow(i, j, D[it], 1);
ins_edge(u, v, 1, (D[it] == S[i][j]) ? 0LL : 1LL);
}
}
}
int f = 0; ll c = 0;
mcmf(s, t, f, c);
printf("%lld\n", c);
return 0;
}
[BZOJ 3172]单词
首先……出题人语文不太好……估计和我这种语文很差的人还有差距……
这个提的意思是,给你若干个单词,输出每个单词在所有单词(而不是把所有单词连起来)中出现的次数。
然后肯定要AC自动机上失配函数搞一波辣……把BFS序倒过来搜,传递出现次数即可。还有这个题的数据范围好像也比给出的小很多……
代码:
/**************************************************************
Problem: 3172
User: danihao123
Language: C++
Result: Accepted
Time:232 ms
Memory:115080 kb
****************************************************************/
#include <cstdio>
#include <cstring>
const int maxn=205;
const int maxnode=1*1e6+5;
#define REP(i,n) for(i=0;i<(n);i++)
#define REP_B(i,n) for(i=1;i<=(n);i++)
#define CL_ARR(x,v) memset(x,v,sizeof(x))
int cnt[maxnode];
namespace ACAutomate{
// Trie
const int sigma_size=26;
int Tree[maxnode][sigma_size];
int siz;
inline int idx(char c){
return c-'a';
}
void InitAC(){
siz=0;
CL_ARR(Tree[0],0);
}
int Insert(char *s){
register int i,n=strlen(s);
register int u=0,c;
REP(i,n){
c=idx(s[i]);
if(!Tree[u][c]){
siz++;
Tree[u][c]=siz;
CL_ARR(Tree[siz],0);
}
u=Tree[u][c];
cnt[u]++;
}
return u;
}
// AC Automate
int f[maxnode];
int Q[maxnode];
void InitFail(){
register int i,u,x,v,head=0,tail=0;
f[0]=0;
REP(i,sigma_size){
u=Tree[0][i];
if(u){
f[u]=0;
Q[tail++]=u;
}
}
while(head<tail){
u=Q[head];
head++;
REP(i,sigma_size){
x=Tree[u][i];
if(!x){
continue;
}
Q[tail++]=x;
v=f[u];
while(v && !Tree[v][i])
v=f[v];
f[x]=Tree[v][i];
}
}
for(i=tail-1;i>=0;i--)
cnt[f[Q[i]]]+=cnt[Q[i]];
}
};
char buf[maxnode];
int pos[maxn];
int main(){
int n;
register int i;
scanf("%d",&n);
ACAutomate::InitAC();
REP_B(i,n){
scanf("%s",buf);
pos[i]=ACAutomate::Insert(buf);
}
ACAutomate::InitFail();
REP_B(i,n){
printf("%d\n",cnt[pos[i]]);
}
return 0;
}