2020 CCPC-Wannafly Winter Camp Day2
A. 托米的字符串
显然答案为:
其中\(f(i)\)表示长度为\(i\)的所有串中含元音的个数。
显然\(f(1)\)易求,那么\(\displaystyle f(2)=f(1)+\sum_{i=2}^{n-1}[s_i\in\{a,e,i,o,u,y\}],f(3)=f(2)+\sum_{i=3}^{n-2}\),同理可递推出其余的\(f\)。然后代入上式求解即可。
当然也可以直接分析每个原因对每个长度的贡献,最终发现贡献呈现“梯形”:即先上升,然后持平,然后下降。
因为贡献的变化是连续的,所以可以直接作两次前缀和即可得出答案。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/1/13 13:30:23
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
char s[N];
bool ok(char c) {
return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' || c == 'y';
}
ll sum[N];
void run(){
cin >> (s + 1);
int n = strlen(s + 1);
for(int i = 1; i <= n; i++) if(ok(s[i])) {
int Min = min(i, n - i + 1), Max = max(i, n - i + 1);
++sum[1];
--sum[Min + 1];
--sum[Max + 1];
}
for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];
for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];
long double ans = 0;
for(int i = 1; i <= n; i++) ans += (long double)1.0 * sum[i] / i;
ans /= (long double)1.0 * n * (n + 1) / 2;
printf("%.10Lf", ans);
}
int main() {
run();
return 0;
}
B. 萨博的方程式
题意大致为求解一个含有\(n\)个变量,每个变量有上限的一个异或方程。
容易想到,我们按二进制位从高到低来考虑。
假设考虑二进制位\(k\)作为最高位的情况,如果\(x_i\)此位为\(1\),就有两种情况:一种是这位取\(1\)的时候,则后面的取值有\((x_i\&((1<<k)-1))\)种情况;这一位取\(0\)时,后面的取值有\((1<<k)\)种情况;如果\(x_i\)此时为\(0\),那么后面就有\((x_i\&((1<<k)-1))\)种情况。
那么我们用背包的思想\(dp\)出该位取值的所有情况。
现有一个观察:就是当最高位为\(1\),但取值为\(0\)时,后面可以任意选,那么对于其它\(x\)无论取哪种值,最后都可以存在一个合法解。
所以若最高位不全为\(1\)时,我们除以\(2^k\)即可得到当前这位的方案数;若全为\(1\)时,以\(k-1\)为最高位继续搞即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/9 9:23:17
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 50 + 5, MOD = 1e9 + 7;
int n, K;
int a[N], f[N][N];
int qpow(ll a, ll b) {
ll res = 1;
while(b) {
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int solve(int k) {
if(k < 0) return 1;
memset(f, 0, sizeof(f));
f[0][0] = 1;
int t = 0, ans = 0;
for(int i = 1; i <= n; i++) {
if(a[i] >> k & 1) {
++t;
for(int j = 0; j <= t; j++) {
if(j) f[t][j] = 1ll * f[t - 1][j - 1] * ((a[i] & ((1 << k) - 1)) + 1) % MOD;
f[t][j] = (f[t][j] + 1ll * f[t - 1][j] * (1 << k) % MOD) % MOD;
}
} else {
for(int j = 0; j <= t; j++) {
f[t][j] = 1ll * f[t][j] * ((a[i] & ((1 << k) - 1)) + 1) % MOD;
}
}
}
int i, inv = qpow(1 << k, MOD - 2);
for(i = (K >> k) & 1; i < t; i += 2) {
ans += 1ll * inv * f[t][i] % MOD;
if(ans >= MOD) ans %= MOD;
}
if(i == t) ans = (ans + solve(k - 1)) % MOD;
return ans;
}
void run(){
for(int i = 1; i <= n; i++) cin >> a[i];
cout << solve(30) << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while(cin >> n >> K) run();
return 0;
}
C. 纳新一百的石子游戏
假设当前异或和为\(x\),显然我们要寻找某个位置\(y\),满足\(y\ xor\ x<y\)。
假设\(x\)的二进制最高位为\(k\),那么所有二进制位为\(k\),此时必有\(y\ xor\ x<y\)。
代码如下:
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 1e5+5,MAXM = 1e6+5,MOD = 998244353,INF = 0x3f3f3f3f,N=2e5;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-7;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
#define x first
#define y second
using namespace std;
int n,cnt[66];
ll a[MAXN];
int main(){
ios::sync_with_stdio(false);cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
ll x=0;
for(int i=1;i<=n;i++){
for(int j=0;j<60;j++){
if(a[i]>>j&1)cnt[j]++;
}
x^=a[i];
int ans=0;
for(int j=59;j>=0;j--){
if(x>>j&1){
ans += cnt[j];
break;
}
}
cout<<ans<<'\n';
}
return 0;
}
D. 卡拉巴什的字符串
题目即是要求对于每个前缀的所有后缀的\(lcp\)长度的\(mex\)值。
假设最长的\(lcp\)长度为\(x\),那么\(0\)~\(x\)所有的值都存在,答案即为\(x+1\)。当然要注意特判\(0\)的情况:当字符串所有字符都相同时,\(0\)的情况不会出现。
那么直接考虑后缀自动机\(|endpos|>1\)的所有结点,将这些结点的\(len\)取\(max\)即可。
当然,深度越大的结点(除开只包含单个位置的结点)他们的\(len\)值也最大,所以也可以直接找\(np\)的父亲结点取\(max\)即可。
两种的时间复杂度都为\(O(n)\)。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/8 16:35:25
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
int n;
char s[N];
struct node{
int ch[26];
int len, fa;
node(){memset(ch, 0, sizeof(ch)), len = 0;}
}dian[N << 1];
int last, tot, cnt;
bool chk[N];
void init() {
last = tot = 1;
cnt = dian[1].len = 0;
for(int i = 1; i <= 2 * n; i++) memset(dian[i].ch, 0, sizeof(dian[i].ch));
}
void add(int c) {
int p = last;
int np = last = ++tot;
dian[np].len = dian[p].len + 1;
for(; p && !dian[p].ch[c]; p = dian[p].fa) dian[p].ch[c] = np;
if(!p) {
dian[np].fa = 1;
++cnt;
if(cnt == 2) chk[0] = true;
}
else {
int q = dian[p].ch[c];
if(dian[q].len == dian[p].len + 1) dian[np].fa = q, chk[dian[q].len] = true;
else {
int nq = ++tot; dian[nq] = dian[q];
dian[nq].len = dian[p].len + 1;
dian[q].fa = dian[np].fa = nq;
chk[dian[nq].len] = true;
for(; p && dian[p].ch[c] == q; p = dian[p].fa) dian[p].ch[c] = nq;
}
}
}
void run(){
cin >> (s + 1);
n = strlen(s + 1);
for(int i = 0; i <= n + 1; i++) chk[i] = false;
init();
int ans = 0;
for(int i = 1; i <= n; i++) {
add(s[i] - 'a');
while(chk[ans]) ++ans;
cout << ans << " \n"[i == n];
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
int T; cin >> T;
while(T--) run();
return 0;
}
E. 阔力梯的树
直接启发式合并即可,复杂度\(O(nlog^2n)\)。
一开始用\(set\)空间复杂度消耗太大了,后来直接用的\(map\),问题不大。
Code
/*
* Author: heyuhhh
* Created Time: 2020/1/13 14:48:19
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
map <int, int> s[N];
int n;
int sz[N];
vector <int> g[N];
ll ans[N];
void dfs(int u) {
for(auto v : g[u]) {
dfs(v);
sz[u] += sz[v];
}
}
//merge y to x
void merge(int x, int y) {
for(auto it : s[y]) {
if(it.fi < 0 || it.fi == INF) continue;
auto p = s[x].lower_bound(it.fi);
auto p2 = p; --p2;
if(p2 -> fi <= 0) {
ans[x] += 1ll * (p -> fi - it.fi) * (p -> fi - it.fi);
} else if(p -> fi >= INF) {
ans[x] += 1ll * (p2 -> fi - it.fi) * (p2 -> fi - it.fi);
} else {
ans[x] -= 1ll * (p -> fi - p2 -> fi) * (p -> fi - p2 -> fi);
ans[x] += 1ll * (p -> fi - it.fi) * (p -> fi - it.fi) + 1ll * (p2 -> fi - it.fi) * (p2 -> fi - it.fi);
}
s[x][it.fi] = 1;
}
}
void dfs2(int u) {
int bson = -1, Max = 0;
for(auto v : g[u]) {
if(sz[v] > Max) {
Max = sz[v];
bson = v;
}
dfs2(v);
}
if(bson != -1) {
ll t = ans[bson];
merge(bson, u);
s[u].swap(s[bson]);
swap(ans[u], ans[bson]);
ans[bson] = t;
}
//dbg(u, ans[u]);
//for(auto it = s[u].begin(); it != s[u].end(); ++it) {
//dbg(it -> fi);
//}
for(auto v : g[u]) if(v != bson) {
merge(u, v);
}
}
void run(){
for(int i = 2; i <= n; i++) {
int p; cin >> p;
g[p].push_back(i);
}
for(int i = 1; i <= n; i++) {
s[i][i] = 1;
s[i][-INF] = 1;
s[i][INF] = 1;
sz[i] = 1;
}
dfs(1);
dfs2(1);
for(int i = 1; i <= n; i++) cout << ans[i] << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
while(cin >> n) run();
return 0;
}
F. 采蘑菇的克拉莉丝
考虑暴力的做法,对于不同的起点,回答每个询问时,枚举起点及其所有出边,然后对于每颗子树利用\(dfs\)序+树状数组计算答案即可。
但这样时间复杂度为\(O(n^2)\)的,接下来考虑优化。
我们枚举出边时,只枚举重儿子的边和往父亲的边,只剩下轻边的贡献没统计。
在进行\(add\)操作时,我们直接从每个结点往上面跳,因为轻重链的个数不会超过\(O(logn)\),在轻边时往父亲结点打上标记,提前算好贡献即可。
时间复杂度为\(O(nlogn)\)。
代码如下:
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/8 8:47:20
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e6 + 5;
int n, q;
struct Edge {
int v, w, next;
}e[N << 1];
int head[N], tot;
void adde(int u, int v, int w) {
e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
}
int dfn, T;
int l[N], r[N], fa[N], w[N];
int bson[N], sz[N], top[N];
void dfs(int u, int f, int v) {
int Max = 0;
sz[u] = 1;
l[u] = ++dfn;
fa[u] = f, w[u] = v;
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v == f) continue;
dfs(v, u, e[i].w);
if(sz[v] > Max) {
bson[u] = v;
Max = sz[v];
}
sz[u] += sz[v];
}
r[u] = dfn;
}
ll c[N];
int lowbit(int x) {return x & (-x);}
void add(int x, int v) {
for(; x <= n; x += lowbit(x)) c[x] += v;
}
ll query(int x) {
ll res = 0;
for(; x > 0; x -= lowbit(x)) res += c[x];
return res;
}
ll query(int L, int R) {
return query(R) - query(L - 1);
}
void dfs2(int u, int f, int topf) {
top[u] = topf;
if(bson[u] != 0) {
dfs2(bson[u], u, topf);
}
for(int i = head[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if(v != bson[u] && v != f) dfs2(v, u, v);
}
}
ll ans[N];
void run(){
memset(head, -1, sizeof(head)), tot = 0;
cin >> n;
for(int i = 1; i < n; i++) {
int u, v, w; cin >> u >> v >> w;
adde(u, v, w); adde(v, u, w);
}
dfs(1, 0, 0);
dfs2(1, 0, 1);
cin >> q;
ll t = 0;
int s = 1;
while(q--) {
int op; cin >> op;
if(op == 1) {
int v, x; cin >> v >> x;
t += x;
add(l[v], x);
while(top[v] != 1) {
ans[fa[top[v]]] += 1ll * w[top[v]] * x;
v = fa[top[v]];
}
} else {
int v; cin >> v;
s = v;
}
ll res = ans[s];
res += 1ll * w[bson[s]] * query(l[bson[s]], r[bson[s]]);
res += 1ll * w[s] * (t - query(l[s], r[s]));
cout << res << '\n';
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
H. 叁佰爱抠的序列
考虑\(k\)个点的完全图,若\(k\)为奇数,那么所有点的度数为偶数,则存在一条欧拉回路;若\(k\)为偶数,那么将结点两两配对连边,最终所有点的度数也为偶数。
因此,在通过二分/贪心/小心枚举算出\(m\)过后,直接建图跑欧拉回路即可。
细节可能有点点多。
Code
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=(a); i<(b); i++)
#define REPE(i,a,b) for(int i=(a); i<=(b); i++)
#define PERE(i,a,b) for(int i=(a); i>=(b); i--)
using namespace std;
typedef long long ll;
ll calceven(ll x) {
return ((ll)sqrtl((long double)(x*2)));
}
ll calcodd(ll x) {
return (1+(ll)sqrtl((long double)(x*8-7)))/2;
}
#define MAXN 4000007
struct bian {
int v, nxt;
};
bian e[MAXN];
int hd[MAXN], tot;
bool vis[MAXN];
void init() {
memset(hd,-1,sizeof hd);
memset(vis,0,sizeof vis);
tot=0;
}
void adde(int u, int v) {
e[tot]=(bian){v,hd[u]};
hd[u]=tot++;
e[tot]=(bian){u,hd[v]};
hd[v]=tot++;
}
int ans[MAXN], ansn;
void go(int u) {
for(int now=hd[u]; ~now; now=e[now].nxt) {
if(!vis[now>>1]) {
vis[now>>1]=1;
go(e[now].v);
ans[ansn++]=e[now].v+1;
}
}
}
int main() {
ll n;
scanf("%lld", &n);
if(n==2) {puts("2\n1 2"); return 0;}
if(n==3) {puts("2\n1 2 1"); return 0;}
ll m1=calceven(n), m2=calcodd(n);
if(m1&1) m1--;
if(!(m2&1)) m2--;
if(m1<3) m1=m2;
else m1=max(m1,m2);
init();
printf("%lld\n", m1);
if(n>2000000) return 0;
if(m1&1) { //odd
REP(i,0,m1) REP(j,i+1,m1) {
adde(i,j);
}
} else { //even
REP(i,0,m1) REP(j,i+1,m1) {
adde(i,j);
}
for(int i=1; i<m1-1; i+=2) {
adde(i,i+1);
}
}
ansn=0;
go(0);
bool fi=false;
REP(i,ansn,n) {
if(fi) putchar(' '); else fi=true;
putchar('1');
}
while(ansn--) {
printf(" %d", ans[ansn]);
}
putchar('\n');
}
I. 堡堡的宝藏
网格图可以看作一个二分图,然后题目给出了一系列\(x+y\geq w(x,y)\)的条件。
回忆\(KM\)算法,左部顶标\(l(x)\),右部顶标\(r(x)\),无论何时都满足\(l(x_i)+r(y_i)\geq w(x_i,y_i)\),刚好符合题目中的限制条件。
而\(KM\)算法寻找的是相等子图的完备匹配,那么最后通过\(KM\)算法求得的答案,即为\(\sum l(x)+\sum r(y)\)最小的情况,同时也是\(\sum w\)最大的情况。
所以该题直接施展\(KM\)算法即可。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/8 19:53:15
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1500 + 5;
namespace R {
int n;
int w[N][N], kx[N], ky[N], py[N], vy[N], slk[N], pre[N];
ll KM() {
fill(kx, kx + n + 1, 0);
fill(ky, ky + n + 1, 0);
fill(py, py + n + 1, 0);
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
kx[i] = max(kx[i], w[i][j]);
for(int i = 1; i <= n; i++) {
fill(vy, vy + n + 1, 0);
fill(slk, slk + n + 1, INF);
fill(pre, pre + n + 1, 0);
int k = 0, p = -1;
for(py[k = 0] = i; py[k]; k = p) {
int d = INF;
vy[k] = 1;
int x = py[k];
for(int j = 1; j <= n; j++)
if (!vy[j]) {
int t = kx[x] + ky[j] - w[x][j];
if (t < slk[j]) { slk[j] = t; pre[j] = k; }
if (slk[j] < d) { d = slk[j]; p = j; }
}
for(int j = 0; j <= n; j++)
if (vy[j]) { kx[py[j]] -= d; ky[j] += d; }
else slk[j] -= d;
}
for (; k; k = pre[k]) py[k] = py[pre[k]];
}
ll ans = 0;
for(int i = 1; i <= n; i++) ans += kx[i] + ky[i];
return ans;
}
}
int n, m, k;
int id[N];
int t1, t2;
int get(int x, int y) {
return (x - 1) * m + y;
}
void run(){
cin >> n >> m >> k;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++) {
id[get(i, j)] = ((i + j) & 1) ? ++t1 : ++t2;
}
}
for(int i = 1; i <= k; i++) {
int x1, x2, y1, y2, w;
cin >> x1 >> y1 >> x2 >> y2 >> w;
if((x1 + y1) & 1) swap(x1, x2), swap(y1, y2);
R::w[id[get(x1, y1)]][id[get(x2, y2)]] = max(R::w[id[get(x1, y1)]][id[get(x2, y2)]], w);
}
R::n = max(t1, t2);
ll ans = R::KM();
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
J. 邦邦的2-SAT模板
给出一份2-SAT的模板:
模板
#include<cstdio>
using namespace std;
const int N=3010;
int g[N<<1],nxt[N<<1],v[N<<1],num;
int q[N<<1],t;
bool vis[N<<1];
int CNT;
int n,m;
void add(int x,int y){
nxt[++num]=g[x];
v[num]=y;
g[x]=num;
}
bool dfs(int x){
CNT++;
if(vis[x>n?x-n:x+n])return 0;
if(vis[x])return 1;
vis[q[++t]=x]=1;
for(int i=g[x];i;i=nxt[i])if(!dfs(v[i]))return 0;
return 1;
}
bool solve(){
for(int i=1;i<=n;i++)if(!vis[i]&&!vis[i+n]){
t=0;
if(!dfs(i)){
while(t)vis[q[t--]]=0;
if(!dfs(i+n))return 0;
}
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
if(x<0)x=n-x;if(y<0)y=n-y;
add(x>n?x-n:x+n,y);add(y>n?y-n:y+n,x);
}
solve();
return 0;
}
构造如下:
\(1\leq i<n,-i\rightarrow i+1\)
\(-n\rightarrow -n\)
因为模板中要从\(i\)找到\(-i\)才罢休,这样构造出一条链的形式,最终复杂度就为\(O(n^2)\)的。
画个图就显然了。
Code
/*
* Author: heyuhhh
* Created Time: 2020/2/9 10:43:20
*/
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
#include <set>
#include <map>
#include <queue>
#include <iomanip>
#define MP make_pair
#define fi first
#define se second
#define sz(x) (int)(x).size()
#define all(x) (x).begin(), (x).end()
#define INF 0x3f3f3f3f
#define Local
#ifdef Local
#define dbg(args...) do { cout << #args << " -> "; err(args); } while (0)
void err() { std::cout << '\n'; }
template<typename T, typename...Args>
void err(T a, Args...args) { std::cout << a << ' '; err(args...); }
#else
#define dbg(...)
#endif
void pt() {std::cout << '\n'; }
template<typename T, typename...Args>
void pt(T a, Args...args) {std::cout << a << ' '; pt(args...); }
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
//head
const int N = 1e5 + 5;
void run(){
int n; cin >> n;
cout << n << '\n';
for(int i = 1; i < n; i++) cout << -i << ' ' << i + 1 << '\n';
cout << -n << ' ' << -n << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cout << fixed << setprecision(20);
run();
return 0;
}
K. 破忒头的匿名信
显然,最暴力的想法就是直接在\(AC\)自动机上\(dp\),但是每次暴力跳\(fail\)指针复杂度可能会达\(O(n^2)\)。
但这个题有个性质不容易被注意到,就是所有字符串长度的总和不超过\(5\cdot 10^5\),也就是说按照长度分类,字符串只有\(O(\sqrt{n})\)级别。
记得处理\(fail\)指针的时候优化一下,就是直接跳到为单词末尾的结点,直接略过那些没用的结点。这样复杂度就是比较严格的\(O(n\sqrt{n})\)了。
代码如下:
Code
#include<bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
const int MAXN = 5e5+5,MAXM = 1e6+5,MOD = 998244353,INF = 0x3f3f3f3f,N=2e5;
const ll INFL = 0x3f3f3f3f3f3f3f3f;
const db eps = 1e-7;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define pii pair<int,int>
#define vii vector<pii>
#define vi vector<int>
#define x first
#define y second
using namespace std;
int n,p[MAXN],len[MAXN],ed[MAXN],last[MAXN],ch[MAXN][26],g[MAXN],tot;
char s[MAXN],s2[MAXN];
ll f[MAXN];
void insert(char *s,int k){
int now=0;
for(int i=1;i<=len[k];i++){
if(!ch[now][s[i]-'a']){
ch[now][s[i]-'a'] = ++tot;
memset(ch[tot],0,sizeof(ch[tot]));
ed[tot] = last[tot] = g[tot] = 0;
}
now = ch[now][s[i]-'a'];
}
if(ed[now]){
if(p[ed[now]] > p[k])ed[now] = k;
}else ed[now] = k;
}
void build(){
queue<int> q;
for(int i=0;i<26;i++){
if(ch[0][i]){
last[ch[0][i]] = g[ch[0][i]]=0;
q.push(ch[0][i]);
}
}
while(q.size()){
int x = q.front();q.pop();
for(int i=0;i<26;i++){
if(ch[x][i]){
last[ch[x][i]] = ch[last[x]][i];
if(ed[last[ch[x][i]]])g[ch[x][i]] = last[ch[x][i]];
else g[ch[x][i]] = g[last[ch[x][i]]];
q.push(ch[x][i]);
}else ch[x][i] = ch[last[x]][i];
}
}
}
void init(){
tot=0;
memset(ch[0],0,sizeof(ch[0]));
ed[0] = last[0] = 0;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
while(cin>>n){
init();
for(int i=1;i<=n;i++){
cin>>(s2+1) >> p[i];
len[i] = strlen(s2+1);
insert(s2,i);
}
cin>>(s+1);
build();
int x=0;
int length = strlen(s+1);
for(int i=1;i<=length;i++){
f[i] = INFL;
x = ch[x][s[i]-'a'];
for(int y=x;y;y=g[y]){
if(ed[y] && f[i-len[ed[y]]] != INFL){
//cout<<i<<' '<<y<<'\n';
f[i] = min(f[i], f[i-len[ed[y]]] + p[ed[y]]);
}
}
}
if(f[length] == INFL)cout<<"-1\n";
else cout<<f[length]<<'\n';
}
return 0;
}
重要的是自信,一旦有了自信,人就会赢得一切。