杭电多校第二场
1005-Everything Is Generated In Equal Probability[期望递推]
如果猜的话就是:\((n^2-1)/9\)
暴力跑一下得到样例是怎么出来的 然后猜测一下…….
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 998244353;
const ll night=443664157;
int main() {
ll n;
while(cin>>n){
cout<<(n*n%mod-1+mod)%mod*night%mod<<endl;
}
return 0;
}
让我们来探讨一下正解.
一般做期望的题思路要清晰,要算某个可以算的期望然后获得对答案的贡献.本题最里层的问题就是求逆序对的期望.那每一个点对\((x,y)\)都有\(\frac{1}{2}\)的概率有贡献.易得一个长为\(n\)的序列逆序对的期望为\(\frac {C_{n}^{2}}{2}\)
一个长为\(i\)的序列的子序列有\(2^i\)个
每个子序列的概率为\(\frac {1}{2^i}\)
长度为\(j\)的子序列的个数为\(C_{i}^j\)
对本题而言,长度为\(i\)的长为\(i\)的子序列期望\(f(i)\)等于所有产生的子序列的期望加起来
\[f(i)=\frac {1}{2^i}\sum_{j=0}^{i}C_{i}^jf(j)+\frac {C_{n}^{2}}{2} \]
左右两边同乘以\(2^i\)
\[f(i)=\frac{1}{2^{i-1}}\sum_{j=0}^{i-1}C_{i}^jf(j)+ {C_{n}^{2}}2^{i-1} \]
就得到一个递推式子 \(O(n^2)\)处理一下
1008-Harmonious Army[最小割建模]
可以看一下2016年国家集训队论文“网络流的一些建模方法”
这题应该从最小割角度建模
把每条边当做有权边 求最小的边集让S与T不连通
再用总边权减去最小割
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
const int maxm = 1e6 + 7;
#define ll long long
const ll inf = 0x3f3f3f3f3f3f3f3f;
struct Dinic {
struct Edge {
int next, to;
ll f;
} e[maxm];
int head[maxn], dep[maxn], tol;
ll ans;
int cur[maxn];
int src, sink, n;
void add(int u, int v, ll f) {
tol++;
e[tol].to = v;
e[tol].next = head[u];
e[tol].f = f;
head[u] = tol;
tol++;
e[tol].to = u;
e[tol].next = head[v];
e[tol].f = 0;
head[v] = tol;
}
bool bfs() {
queue<int> q;
memset(dep, -1, sizeof(dep));
q.push(src);
dep[src] = 0;
while (!q.empty()) {
int now = q.front();
q.pop();
for (register int i = head[now]; i; i = e[i].next) {
if (dep[e[i].to] == -1 && e[i].f) {
dep[e[i].to] = dep[now] + 1;
if (e[i].to == sink) return true;
q.push(e[i].to);
}
}
}
return false;
}
ll dfs(int x, ll maxx) {
if (x == sink) return maxx;
for (register int &i = cur[x]; i; i = e[i].next) {
if (dep[e[i].to] == dep[x] + 1 && e[i].f > 0) {
ll flow = dfs(e[i].to, min(maxx, e[i].f));
if (flow) {
e[i].f -= flow;
e[i ^ 1].f += flow;
return flow;
}
}
}
return 0;
}
ll dinic(int s, int t) {
ans = 0;
this->src = s;
this->sink = t;
while (bfs()) {
for (register int i = 0; i <= n; i++)
cur[i] = head[i];
while (ll d = dfs(src, inf))
ans += d;
}
return ans;
}
void init(int n) {
this->n = n;
for(int i=0;i<=n;++i) head[i]=0;
tol = 1;
}
} G;
int main() {
int n,m;
while(scanf("%d%d",&n,&m)!=EOF){
ll u,v,a,b,c;
ll sum=0;
G.init(n+10);
int S=0,T=n+2;
for(int i=1;i<=m;++i){
scanf("%lld%lld%lld%lld%lld",&u,&v,&a,&b,&c);
sum+=(a+b+c);
G.add(S,u,a+b);
G.add(S,v,a+b);
G.add(u,T,b+c);
G.add(v,T,b+c);
G.add(u,v,a+c-b*2);
G.add(v,u,a+c-b*2);
}
printf("%lld\n",sum-G.dinic(S,T)/2);
}
return 0;
}
1009-I Love Palindrome String[回文树]
回文树的模板题
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN = 300005;
const int N = 26;
int ans[MAXN];
struct PAT {
struct node {
int len, num, fail, son[N];
} t[MAXN];
int x[MAXN];
short s[MAXN];
bool vis[MAXN];
int sum[MAXN];
int tot, last, n;
void init(int len) {
for (register int i = 0; i < len + 5; ++i) {
t[i].len = t[i].num = t[i].fail = 0;
for (register int j = 0; j < 26; ++j) t[i].son[j] = 0;
x[i] = vis[i] = 0;
sum[i] = 0;
}
tot = last = 1;
n = 0;
t[0].len = 0, t[1].len = -1;
t[0].fail = t[1].fail = 1;
s[0] = -1;
}
void add(int c) {
int p = last;
s[++n] = c;
while (s[n] != s[n - 1 - t[p].len])p = t[p].fail;//匹配找到第一个合法的最长后缀回文子串
if (!t[p].son[c]) {//如果没有新的本质不同的回文子串
int v = ++tot, k = t[p].fail;
while (s[n] != s[n - t[k].len - 1])k = t[k].fail;//获得新节点的fail
t[v].fail = t[k].son[c];
t[v].len = t[p].len + 2;
t[v].num = t[t[v].fail].num + 1;
t[p].son[c] = v;
}
last = t[p].son[c];
sum[last]++;//不同位置算不同的
}
void solve() {
for (register int i = tot; i >= 2; --i) {//从后往前
if (x[i] == 0) x[i] = i;
while (x[i] >= 2 && t[x[i]].len > (t[i].len + 1) / 2) {//沿着fail指针找
x[i] = t[x[i]].fail;
}
if (x[i] >= 2 && t[x[i]].len == (t[i].len + 1) / 2) {//若找到长度为其一半的回文子串的节点
vis[i] = 1;//说明这个字符串是满足题意的串
}
x[t[i].fail] = x[i];//优化,不需要重复找
sum[t[i].fail] += sum[i];//父亲累加儿子的sum,因为如果fail[v]=u,则u一定是v的子回文串!
if (vis[i]) ans[t[i].len] += sum[i];
}
}
} T;
char str[MAXN];
int main() {
int len;
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
while (cin >> str) {
len = strlen(str);
T.init(len);
for (register int i = 1; i <= len; ++i) {
T.add(str[i-1] - 'a');
ans[i] = 0;
}
T.solve();
for (register int i = 1; i < len; ++i)cout << ans[i] << " ";
cout << ans[len] << endl;
}
return 0;
}
1010-Just Skip The Problem[签到]
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 1e6 + 3;
ll fac[mod];
int main() {
fac[0] = 1;
for (ll i = 1; i < mod; ++i) {
fac[i] = fac[i - 1] * i % mod;
}
int n;
while (scanf("%d", &n) != EOF) {
if (n >= mod) {
puts("0");
} else {
printf("%lld\n", fac[n]);
}
}
return 0;
}
1011-Keen On Everything But Triangle[主席树+斐波那契数列性质]
知道正着来只需要44次就能得到三角形
反着来也要想到
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a[100005], n, m, copn;
vector<ll> v;
struct Node {
ll l, r, sum;
} node[100005 * 20];
ll tot = 0;
ll tree[100005];
ll getpos(ll x) {
return lower_bound(v.begin(), v.end(), x) - v.begin() + 1;
}
ll build(ll l, ll r) {
ll now = ++tot;
node[now].sum = 0;
if (l == r)
return now;
ll mid = (l + r) >> 1;
node[now].l = build(l, mid);
node[now].r = build(mid + 1, r);
return now;
}
ll update(ll last, ll pos) {
ll now = ++tot, ret = now;
node[now].sum = node[last].sum + 1;
ll l = 1, r = n;
while (l < r) {
ll mid = (l + r) >> 1;
if (pos <= mid) {
node[now].l = ++tot, node[now].r = node[last].r;
last = node[last].l, now = tot;
r = mid;
} else {
node[now].l = node[last].l, node[now].r = ++tot;
last = node[last].r, now = tot;
l = mid + 1;
}
node[now].sum = node[last].sum + 1;
}
return ret;
}
/*
inline void update(ll pre,ll& cur,ll l,ll r,ll v){
cur=++tot;
num[cur]=num[pre]+1;
if(l==r) return;
ls[cur]=ls[pre],rs[cur]=rs[pre];
ll m=(l+r)>>1;
if(v<=m) update(ls[pre],ls[cur],l,m,v);
else update(rs[pre],rs[cur],m+1,r,v);
}
*/
ll ask_kth(ll ltree, ll rtree, ll k) {
ll l = 1, r = n;
while (l < r) {
ll mid = (l + r) >> 1;
if (node[node[rtree].l].sum - node[node[ltree].l].sum >= k) {
ltree = node[ltree].l, rtree = node[rtree].l;
r = mid;
} else {
k -= (node[node[rtree].l].sum - node[node[ltree].l].sum);
ltree = node[ltree].r, rtree = node[rtree].r;
l = mid + 1;
}
}
return l;
}
void init() {
v.clear();
tot = 0;
for (ll i = 0; i < 100005 * 20; i++)
node[i].sum = 0;//初始化
copn = n;
}
void solve() {
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
n = v.size();//离散化
tree[0] = build(1, n);
for (ll i = 1; i <= copn; i++) tree[i] = update(tree[i - 1], getpos(a[i]));
}
inline ll Query(ll l, ll r, ll cnt) {
return v[ask_kth(tree[l - 1], tree[r], cnt) - 1];
}
int main() {
while (scanf("%lld%lld", &n, &m) != EOF) {
init();
for (ll i = 1; i <= n; i++) scanf("%lld", &a[i]), v.push_back(a[i]);
solve();
while (m--) {
ll l, r;
ll val = -1;
scanf("%lld%lld", &l, &r);//询问[l,r]第k小
ll ans1 = 0, ans2 = 0, ans3 = 0;
for (ll i = 1; i <= (r - l) + 1; i++) {
ll cnt = (r - l) + 2 - i;
if (ans2) ans1 = ans2;
else ans1 = Query(l, r, cnt);
cnt--;
if (cnt < 1) continue;
if (ans3) ans2 = ans3;
else ans2 = Query(l, r, cnt);
cnt--;
if (cnt < 1) continue;
ans3 = Query(l, r, cnt);
if (ans1 < ans2 + ans3) {
val = ans1 + ans2 + ans3;
break;
}
}
printf("%lld\n", val);
}
}
return 0;
}