赛时 rank 17,T1 100,T2 0,T3 0,T4 70
T2一眼OSU的拓展版,懒得打了。
T4写了一个奇怪的做法,轻轻松松拿70?
T3读假题了,然后间接导致了我与STL和pbds斗智斗勇。
题可能不算很难但是我糖
线性只因
用bitset记录每个数在二进制下的每一位,从高位到低位贪心即可。
如果可以的数小于m个,那么就舍弃这一位。
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e6 + 10;
int n,m,a[N];
bitset<N> vis[31];
inline void solve(){
cin>>n>>m;
for(int i = 1;i <= n; ++i) cin>>a[i];
for(int i = 30;i >= 0; --i){
for(int j = 1;j <= n; ++j)
if((a[j]>>i)&1) vis[i][j] = true;
}
bitset<N> res;res.set();
int bg = 0;
for(int i = 30;i >= 0; --i){
if(vis[i].count() >= m){
bg = i;
break;
}
}
res = vis[bg];
int ans = 0;
for(int i = bg;i >= 0; --i){
if((vis[i]&res).count() >= m) ans |= (1<<i),res&=vis[i];
}
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
金箱子
OSU拓展版,就是记录\(x^0,x^1,\dots,x^k\)的期望,递推转移
如何记录\(x^k\)的期望?由二项式定理,有\((x+a)^k=\sum_{i=0}^kC_k^ix^ia^{k-i}\)
然后就这么维护就可以了
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 1e4 + 10,mod = 998244353;
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;
}
int f[N][110],t[N][110],fac[N],inv[N],n,k;
inline int C(int n,int m){return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;}
struct node{int p1,p2,a,b;}a[N];
inline void solve(){
cin>>n>>k;
for(int i = 1;i <= n; ++i) cin>>a[i].p1>>a[i].a>>a[i].b,a[i].p2 = 1-a[i].p1+mod;
fac[0] = 1;
for(int i = 1;i < N; ++i) fac[i] = 1ll * fac[i - 1] * i % mod;
inv[N - 1] = power(fac[N - 1],mod - 2,mod);
for(int i = N - 2;i >= 0; --i) inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
for(int i = 1;i <= n; ++i)
for(int j = 0;j <= k; ++j)
t[i][j] = (1ll*power(a[i].a,j,mod)*a[i].p1%mod+1ll*power(a[i].b,j,mod)*a[i].p2%mod)%mod;
f[1][0] = 1;
for(int i = 1;i <= k; ++i) f[1][i] = t[1][i];
for(int i = 2;i <= n; ++i){
f[i][0] = 1;
for(int j = 1;j <= k; ++j){
for(int k = 0;k <= j; ++k){
f[i][j] = (f[i][j] + 1ll*C(j,k)*f[i-1][k]%mod*t[i][j-k]%mod)%mod;
}
}
}
cout<<f[n][k]<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
可持久化字符串
考虑到每次只是加减一个字符,那么将一个版本插到一颗\(Trie\)上,对于每次增减,直接在上一个版本的末尾处增减即可。
在\(Trie\)树上每一个节点开一个vector,记录当前位置为那些版本的末尾。
将询问离线下来,记录一下。
在\(Trie\)树上跑AC自动机,然后字符串匹配即可。
复杂度 \(O(n)\)。
注意查询时要用拓扑排序,不然会T
赛后因为早版本的大样例有锅被硬控了2h
点此查看代码
#include<bits/stdc++.h>
#include<bits/extc++.h>
// using namespace __gnu_pbds;
// using namespace __gnu_cxx;
using namespace std;
#define infile(x) freopen(x,"r",stdin)
#define outfile(x) freopen(x,"w",stdout)
#define errfile(x) freopen(x,"w",stderr)
#ifdef LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = stdin,*OutFile = stdout;
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define int long long
const int N = 1e5 + 10;
class BIT{
private:
int tree[N];
#define lowbit(x) (x&(-x))
public:
int mx = N-10;
inline void update(int pos,int val){
pos++;
for(int i = pos;i <= mx + 3; i += lowbit(i)) tree[i] += val;
}
inline int query(int pos){
int res = 0;
for(int i = pos; i;i -= lowbit(i)) res += tree[i];
return res;
}
}T;
#define get(x) (x-'a'+1)
class Auto_Maton{
private:
int trie[N][27],fail[N],fa[N],rd[N],cnt[N];
vector<int> pos[N];
public:
Auto_Maton(){pos[0].emplace_back(0);}
int tot;
inline int insert(int from,int id,char s){
int x = get(s);
int p = trie[from][x]?trie[from][x]:trie[from][x] = ++tot;
fa[p] = from;
pos[p].emplace_back(id);
return p;
}
inline int erase(int from,int id){pos[fa[from]].emplace_back(id);return fa[from];}
inline void build(){
queue<int> q;
for(int i = 1;i <= 26; ++i)
if(trie[0][i]) q.push(trie[0][i]),fail[trie[0][i]] = 0;
while(q.size()){
int x = q.front();q.pop();
for(int i = 1;i <= 26; ++i){
if(trie[x][i])
fail[trie[x][i]] = trie[fail[x]][i],q.push(trie[x][i]),++rd[trie[fail[x]][i]];
else trie[x][i] = trie[fail[x]][i];
}
}
}
inline void get_ans(string s){
build();int p = 0;
for(char x:s) p = trie[p][get(x)],++cnt[p];
queue<int> q;
for(int i = 1;i <= tot; ++i) if(!rd[i]) q.push(i);
while(q.size()){
int x = q.front();q.pop();
if(cnt[x]) for(auto k:pos[x]) T.update(k,cnt[x]);
if(fail[x]){
cnt[fail[x]] += cnt[x];
--rd[fail[x]];
if(!rd[fail[x]]) q.push(fail[x]);
}
}
for(auto k : pos[0]) T.update(k,1);
}
}AC;
int n,m,pos[N],cnt;
string b;
vector<pair<int,int> > que;
inline void solve(){
cin>>n>>m>>b;
char x;
for(int i = 1,op,p,l,r;i <= n; ++i){
cin>>op;
if(op == 1){
cin>>p>>x;
++cnt;
pos[cnt] = AC.insert(pos[p],cnt,x);
}
if(op == 2){
cin>>p;cnt++;
pos[cnt] = AC.erase(pos[p],cnt);
}
if(op == 3){
cin>>l>>r;
que.emplace_back(make_pair(l,r));
}
}
AC.get_ans(b);
for(auto i:que){
cout<<T.query(i.second+1)-T.query(i.first)<<'\n';
}
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
圣诞树
写了一个神奇的暴力,就是用bitset记录每一个链的贡献,但是如果树退化成链就会寄,于是就将一条大链再砍成几个小链,暴力跳即可,时间复杂度未知,和\(O(n\sqrt{n})\)的莫队一样都是70pts(都在最后两个点被卡了)
正解是整体二分,不会,贺了
考虑利用每个朋友最多送给 chino 一件礼物这个性质,考虑离线后做整体二分,设当前二分的值为 \(mid\) ,那么我们需要对于每个询问求解树上路径中 \([L,mid]\) 值域中出现的数的种数。
发现所有权值的出现情况只有 \(3\) 种:当前权值只出现 \(1\) 次,当前权值出现 \(2\) 次并且出现的位置具有祖先关系,当前权值出现 \(2\) 次并且出现位置没有祖先关系。
假设所有询问 \((x,y)\) 均满足 \(x\) 的 \(\operatorname{dfn}\) 序小于等于 \(y\) 的 \(\operatorname{dfn}\) 序。
考虑第一种情况会贡献到的询问,设出现的位置为 \(u\) ,容易发现这会贡献到 \(\operatorname{dfn}_x\in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u + \operatorname{size}_u, n]\) 和 \(\operatorname{dfn}_x \in [1, \operatorname{dfn}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1]\) 的询问,需要特殊处理 \(\operatorname{lca}(x, y) = u\) 的询问。
考虑第二种情况,首先按照第一种情况分别处理 \(u, v\) 两点,发现存在一些询问被贡献了两次,设 \(v\) 在 \(u\) 方向上的儿子为 \(p\) ,那么 \(\operatorname{dfn}_x \in [1, \operatorname{dfn}_p - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1]\) 和 \(\operatorname{dfn}_x \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_p + \operatorname{size}_p, n]\) 的询问会被贡献两次。
考虑第三种情况,仍然按照第一种情况分别处理 \(u,v\) 两端,发现 \(\operatorname{dfn}_x \in [\operatorname{dfn}_u, \operatorname{dfn}_u + \operatorname{size}_u - 1], \operatorname{dfn}_y \in [\operatorname{dfn}_v, \operatorname{dfn}_v + \operatorname{size}_v - 1]\) 的询问会被贡献两次。
于是原问题转化为二维平面上做矩形加,单点查询,可以用扫描线解决,复杂度为 \(O(n\log^2n)\) ,使用树状数组后可以在 \(1200ms\) 通过本题。
标签:27,集训,int,long,operatorname,dfn,FILE,using,csp From: https://www.cnblogs.com/hzoi-Cu/p/18374442