讲个笑话:
(讨论时间)
huge:(叹气)这讨论啊,就是改不了,这换了铃声了,也没……
众人:现在是讨论时间啊。
huge:(停顿)那刚才大课间那会哇啦哇啦的……
图书管理
简要题意
给定一个长度为\(n(n\le 10^4)\)的排列,求\(\sum\limits_{l=1}^n\sum\limits_{r=l}^n[r-l为偶数]l\times r\times f_{l,r}\)
solution
签到题,但我\(ccx\)被卡了。
部分分是对顶堆、主席树之类的带\(\log\)做法。
枚举中位数,假设当前枚举到\(i\),然后将大于它的数的记为1,小于它的数的记为-1,求前缀和\(s\),如果\(1\le l\le i\le r\le n\)能产生贡献时当且仅当\(s_r-s_{l-1}=0\)。
直接做就完了,用数组求常数较小,可以直接过。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// #include<sys/timeb.h>
using namespace __gnu_pbds;
using namespace std;
// struct timeb timer;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCALFILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);// FILE *ErrFile = freopen("err.err","w",stderr);
#elseFILE *InFile = freopen("book.in","r",stdin),*OutFile = freopen("book.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
const int N = 1e4 + 10;
int n,a[N];
namespace IO{char buf[1<<23],*p1,*p2;#define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)#define pc putchar_unlockedtemplate<class T>inline void read(T &x){x = 0;char s = gc();for(;s < '0' || s > '9';s = gc());for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);}template<class T,class... Args>inline void read(T &x,Args&... argc){read(x);read(argc...);}template<class T>inline void write(T x){static int sta[20],top = 0;do{sta[++top] = x%10,x /= 10;}while(x);do{pc(sta[top--]+'0');}while(top);}inline void write(char x){pc(x);}template<class T,class... Args>inline void write(T x,Args... argc){write(x);write(argc...);}
}using namespace IO;
int tp[N<<2];
inline void solve(){ll ans = 0;read(n);rep(i,1,n,1) read(a[i]);rep(l,1,n,1){int tot1 = 0,tot2 = 0;queue<int> q;rep(r,l,n,1){tot1 += (a[r] > a[l]),tot2 += (a[r] < a[l]);tp[tot1-tot2+n] += r;}tot1 = tot2 = 0;drep(j,l,1,1){tot1 += a[j] > a[l],tot2 += a[j] < a[l];ans += 1ll*j*tp[tot2-tot1+n]*a[l];}tot1 = tot2 = 0;rep(r,l,n,1){tot1 += (a[r] > a[l]),tot2 += (a[r] < a[l]);tp[tot1-tot2+n] = 0;}} write(ans,'\n');
}
signed main(){// cin.tie(nullptr)->sync_with_stdio(false);solve();
}
两棵树
简要题意
给你两棵大小为\(n(n\le 2\times10^5)\)树,一个节点有\(\frac{1}{2}\)的概率出现在一棵树中,有\(\frac{1}{2}\)的概率出现在另一棵树中,求两棵树连通块个数乘积的期望数。
solution
考虑到连通块数=剩余的点数-剩余的边数,则贡献\(X\times Y\)可以拆成4部分,为\(\text{点}\times\text{点}-\text{边}\times\text{点}-\text{点}\times\text{边}+\text{边}\times\text{边}\)。
-
\(\text{点}\times\text{点}\)的贡献。
考虑在\(T\)中选择点\(x\),\(U\)中选择点\(y\)。若\(x = y\),则贡献为\(0\),反之,贡献为\(\frac{1}{4}\)。所以总贡献为\(\frac{\mathrm{C}_{n}^2}{4}\)
-
\(\text{边}\times\text{点}\)的贡献。
考虑\(T\)中留下边\((x,y)\),\(U\)中留下点\(k\),则当\(x=k\)或\(y=k\)时贡献为0,反之贡献为\(\frac{1}{8}\)。所以总贡献为\(\frac{(n-1)\times (n-2)}{8}\)。
-
\(\text{边}\times\text{边}\)的贡献。
考虑\(T\)中留下\((x,y)\),\(U\)中留下\((u,v)\),当\(x,y,u,v\)中存在两个相同时,贡献为0,反之,贡献为\(\frac{1}{16}\)。
考虑如何计算,可以枚举\((x,y)\),统计符合条件的\((u,v)\)个数,就是\(n-1-deg_{U}x-deg_{U}y+[U\text{中存在}(u,v)=(x,y)]\)
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCALFILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#elseFILE *InFile = freopen("tree.in","r",stdin),*OutFile = freopen("tree.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
constexpr int N = 2e5 + 10,mod = 998244353;
struct node{int x,y;}a[N],b[N];
int n,d[N];
inline int power(int a,int b,int mod){int res = 1;for(;b;b >>= 1,a = 1ll*a*a%mod)if(b&1) res = 1ll*res*a%mod;return res;
}
inline int Inv(int x){return power(x,mod-2,mod);}
set<int> st[N];
inline void solve(){cin>>n;rep(i,1,n-1,1) cin>>a[i].x>>a[i].y;rep(i,1,n-1,1) cin>>b[i].x>>b[i].y,st[b[i].x].insert(b[i].y),st[b[i].y].insert(b[i].x),d[b[i].x]++,d[b[i].y]++;int ans = 0;ans = (ans + 1ll*n*(n-1)%mod*Inv(4)%mod)%mod;ans = (ans - 1ll*(n-1)%mod*(n-2)%mod*Inv(4)%mod + mod) % mod;if(n >= 4) rep(i,1,n-1,1) ans = (ans + 1ll*(n-1-d[a[i].x]-d[a[i].y] + st[a[i].x].count(a[i].y))*Inv(16)%mod)%mod;cout<<ans<<'\n';
}
signed main(){cin.tie(nullptr)->sync_with_stdio(false);solve();
}
函数
简要题意
定义一个函数\(f(x)=(x\oplus a)-b\),其中\(a,b\)为给定的参数。
有一个长度为\(n(n\le 10^6)\)的序列\(x\),给出\(q(q\le 10^6)\)次询问,每次给定\(a,b\),问是否存在\(i\in [1,n)\),满足\(f(x_i)\times f(x_{i+1})\le 0\)。
solution
简单题,可是赛时没想到用Trie。
考虑求出\(x_i\oplus a\)最大的位置\(maxpos\)和最小的位置\(minpos\),如果\(f(x_{maxpos})\times f(x_{minpos}) > 0\)显然无解。
反之,考虑二分,每次取中点\(mid\),易知\(f(x_{mid})\)肯定与\(f(x_{maxpos})\)或\(f(x_{minpos})\)中的一个异号,所以\(f(x_{maxpos})\)或\(f(x_{minpos})\)与\(f(x_{mid})\)中必然有一对异号,正确性显然。
时间复杂度\(O(n\log V + q\times(\log n+\log V))\approx 2e8\)略微卡常。
点此查看代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,s,t,p) for(int i = s;i <= t;i += p)
#define drep(i,s,t,p) for(int i = s;i >= t;i -= p)
#ifdef LOCALFILE *InFile = freopen("in.in","r",stdin),*OutFile = freopen("out.out","w",stdout);
#elseFILE *InFile = freopen("fun.in","r",stdin),*OutFile = freopen("fun.out","w",stdout);
#endif
using ll = long long;using ull = unsigned long long;
using db = double;using ldb = long double;
namespace IO{char buf[1<<23],*p1,*p2;#define gc() (p1==p2&&(p2=(p1=buf)+fread_unlocked(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)#define pc putchar_unlockedtemplate<class T>inline void read(T &x){x = 0;char s = gc();for(;s < '0' || s > '9';s = gc());for(;'0' <= s && s <= '9';s = gc()) x = (x<<1)+(x<<3)+(s^48);}template<class T,class... Args>inline void read(T &x,Args&... argc){read(x);read(argc...);}template<class T>inline void write(T x){static int sta[20],top = 0;do{sta[++top] = x%10,x /= 10;}while(x);do{pc(sta[top--]+'0');}while(top);}inline void write(char x){pc(x);}template<class T,class... Args>inline void write(T x,Args... argc){write(x);write(argc...);}
}using namespace IO;
const int N = 1e6 + 10,V = 1e9 + 10,LV = 29;
int n,q,x[N];
struct Trie{int tree[N*30][2],tot,en[N*30],ex[N*30];inline void insert(int x,int pos){int p = 0;drep(i,LV,0,1){bool k = (x>>i)&1;if(!tree[p][k]) tree[p][k] = ++tot;p = tree[p][k];}if(!en[p]) en[p] = pos;ex[p] = max(ex[p],pos);}inline int qryn(int x){int p = 0;drep(i,LV,0,1){bool k = (x>>i)&1;if(tree[p][k]) p = tree[p][k];else p = tree[p][!k];}return en[p];}inline int qryx(int x){int p = 0;drep(i,LV,0,1){bool k = (x>>i)&1;if(tree[p][!k]) p = tree[p][!k];else p = tree[p][k];}return ex[p];}
}trie;
inline bool cmp(int x,int y){return (x<=0&&y>=0)||(x>=0&&y<=0);}
inline void solve(){read(n,q);rep(i,1,n,1) read(x[i]);rep(i,1,n,1) trie.insert(x[i],i);while(q--){int a,b;read(a,b);int l = trie.qryn(a);int r = trie.qryx(a);if(l > r) swap(l,r);auto f = [&](int pos){return (x[pos]^a)-b;};if(!cmp(f(l),f(r))){puts("-1");continue;}int pos1 = l,pos2 = r,ans = 0;int f1 = f(pos1),f2 = f(pos2);bool flag = true;while(l <= r){int mid = (l + r) >> 1;if(cmp(f2,f(mid))) flag = false,ans = mid,l = mid + 1;else flag = true,ans = mid,r = mid - 1;}if(flag) write(ans-1,'\n');else write(ans,'\n');}
}
signed main(){solve();}
编辑
简要题意
给定\(k(k\le 30)\)和两个字符串\(S,T(|S|,|T|\le 5\times 10^4)\),\(\forall i\in[0,k]\),求出\(T\)中多少个非空子串与\(S\)的编辑距离恰好为\(i\)。
solution
严格弱化版:[BJOI2015] 隐身术
部分分直接暴力DP。正解好像很难的样子,不会。
30pts
枚举 \(T\) 的某一子串进行编辑距离求解的DP,具体状态为让 \(A\) 变成 \(B\),现在只考虑 \(A[1:i]\) 变成 \(B[1:j]\) 的编辑距离为 \(f[i][j]\),转移时考虑删除,添加,修改第 \(i+1\) 个位置即可,时间复杂度为 \(O(n^4)\)。
100pts
枚举每个后缀,\(f_{i,j}\) 表示最大的 \(x\),使得 \(S[1:x]\) 和 \(T[1:x+j]\) 可以在 \(i\) 次编辑内得到,显然若 \(x\) 可以,所有\(x_0 \leq x\), \(S[1:x_0]\) 和 \(T[1:x_0+j]\) 都可以在 \(i\) 次编辑内得到。
考虑如何转移,先考虑做完第 \(j\) 次编辑操作后往外延申,可以延申的即为 \(S\) 的一个后缀和 \(T\) 的一个后缀的最长公共前缀,即\(f_{i,j} = f_{i,j} + \text{LCP}(S[f_{i,j + 1}:|S|],T [f_{i,j} + j + 1 . .: |T|])\),随后我们可以通过对\(f_{i+1,j-1},f_{i+1,j},f_{i+1,j+1}\) 三项进行转移,即考虑下一次的编辑的具体操作是删除添加还是修改。
每次要算的两个后缀的前缀下标不会差超过 \(k\),因此一共至多求 \(O(nk)\) 次 LCP,可以利用二分+ hash 的方式解决。
记录每个后缀中 \(f_{i,j}=|S|\) 的那些状态,即可计算出最终答案,时间复杂度为 \(O(nk^2+nk \log n)\)。