多校A层冲刺NOIP2024模拟赛04
学校OJ赛时 rank28,T1 0pts,T2 100pts,T3 20pts,T4 25pts
accoders rank15,T1 100pts,T2 100pts,T3 20pts,T4 25pts
不是,也没人告诉我两个OJ文件名不一样啊
02表示法
递归 + 高精除低精。
点此查看代码
#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)
#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 LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("power.in"),*OutFile = outfile("power.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 200;
string s;
ll n;
inline int divide(string &s){
if(!s.size()) return -1;
if(s.size() == 1 && s[0] == '0') return -1;
if(s.size() == 1 && s[0] == '1'){
s = "";return 1;
}
int res = (s.back() - '0')%2;s.back() -= res;
int n = s.size(),x = 0;
string sh;bool flag = false;
rep(i,0,n-1,1){
x = x*10 + (s[i] - '0');
if(x >= 2) sh.push_back(x/2+'0'),x %= 2,flag = true;
else if(flag) sh.push_back('0');
}
s = sh;
return res;
}
#define pii pair<int,int>
#define mk make_pair
void work(string s){
if(s == "0") return cout<<0,void();
if(s == "1") return cout<<"1",void();
if(s == "2") return cout<<"2",void();
if(!s.size()) return;
vector<pii> p;
rep(i,0,999999,1){
int x = divide(s);
if(x == -1) break;
if(!x) continue;
p.emplace_back(mk(i,1));
}
reverse(p.begin(),p.end());
int len = p.size();
for(int i = 0;i < len; ++i){
if(p[i].first == 1) {cout<<2;goto ed;}
cout<<2;
if(p[i].first != 1) cout<<'(';
work(to_string(p[i].first));
if(p[i].first != 1) cout<<')';
ed:;
if(i != len - 1) cout<<'+';
}
}
inline void solve(){
cin>>s;
work(s);
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
子串的子串
赛时写了一个\(O(nq\log n)\)的暴力,本来是能拿70pts的,但是常数小+信仰就过了。
就是把每次查询的串截出来,然后跑个后缀数组求\(height\),用结论 : 一个字符串本质不同的子串的数量为\(\frac{n\times (n+1)}{2}-\sum_{i=2}^n height_i\),就可以了。
求后缀数组用的倍增法,用\(DC3\)的话就是\(O(nq)\)的
点此查看代码
#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)
#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 LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("substring.in"),*OutFile = outfile("substring.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 3010;
char *p1,*p2,buf[1<<23];
#define gc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<23,stdin),p1==p2)?EOF:*p1++)
#ifdef linux
#define pc putchar_unlocked
#else
#define pc putchar
#endif
namespace IO{
template<typename T>inline bool read(T &x){x=0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x<<1)+(x<<3)+(s^48);if(!f) x=~x+1;return true;}
inline bool read(double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(long double &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(float &x){x=0.0;char s=gc();bool f=true;for(;(s<'0'||'9'<s);s=gc()) {if(s=='-') f=false;if(s==EOF)return false;}for(;'0'<=s&&s<='9';s=gc()) x=(x*10)+(s^48);if(s!='.'){return true;}double res=0.1;s=gc();for(;'0'<=s&&s<='9';res/=10,s=gc()) x+=(s^48)*res;x=f?x:-x;return true;}
inline bool read(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false; for(;s!=' '&&s!='\n'&&s!=EOF;s=gc())str.push_back(s);return true;}
inline bool read_line(string &str){string ().swap(str);char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str.push_back(s);}return true;}
inline bool read_line(char *str){int len=0;char s=gc();for(;s==' '||s=='\n';s=gc());if(s==EOF) return false;for(;s!='\n'&&s!=EOF;s=gc()){str[len]=s;len++;}str[len]='\0';return true;}
inline bool read(char &s){char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF||x==' '||x=='\n')return false;s=x;return true;}
inline bool read(char *s){int len=0;char x=gc();for(;x==' '||x=='\n';x=gc());if(x==EOF)return false;for(;x!=' '&&x!='\n'&&x!=EOF;x=gc())s[len++]=x;s[len]='\0';return true;}
template<class T,class... Args> inline bool read(T &x,Args&... args){return (read(x)&&read(args...));}
template<class T>inline void write(T x){static T st[45];int top=0;if(x<0)x=~x+1,pc('-');do{st[top++]=x%10;}while(x/=10);while(top)pc(st[--top]^48);}
inline void write(char x){pc(x);}
inline void write(string s){for(int i=0;s[i];++i) pc(s[i]);}
inline void write(char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
inline void write(const char *s){int len=strlen(s);for(int i=0;i<len;++i) pc(s[i]);}
template<class T,class... Args> inline void write(T x,Args... args){write(x);write(args...);}
}using namespace IO;
int n,m,sa[N],rk[N],tp[N],ht[N],ct[N];char s[N],s1[N];
inline void Qsort(int m){
rep(i,1,m,1) ct[i] = 0;
rep(i,1,n,1) ++ct[rk[i]];
rep(i,2,m,1) ct[i] += ct[i-1];
drep(i,n,1,1) sa[ct[rk[tp[i]]]--] = tp[i];
}
inline bool cmp(int x,int y,int w){return (tp[x]==tp[y]&&tp[x+w]==tp[y+w]);}
inline void get_sa(){
n = strlen(s+1);
int m = 27;
rep(i,1,n,1) rk[i] = s[i] - 'a' + 1,tp[i] = i;
Qsort(m);
for(int w=1,p=0;w<=n;m=p,p=0,w<<=1){
rep(i,n-w+1,n,1) tp[++p]=i;
rep(i,1,n,1) if(sa[i]>w) tp[++p]=sa[i]-w;
Qsort(m);
rep(i,1,n,1) swap(tp[i],rk[i]);
p=rk[sa[1]]=1;
rep(i,2,n,1) rk[sa[i]]=cmp(sa[i-1],sa[i],w)?p:++p;
if(p==n) break;
}
int k = 0;
rep(i,1,n,1){
if(!rk[i]) continue;
if(k) --k;
while(s[i+k] == s[sa[rk[i]-1]+k]) k++;
ht[rk[i]] = k;
}
}
inline void solve(){
read(n,m);read(s1+1);
while(m--){
int l,r;ll ans;read(l,r);
rep(i,l,r,1) s[i-l+1] = s1[i];
n = r-l+1;
s[n+1] = '\0';
get_sa();
ans = 1ll*n*(n+1)/2;
rep(i,2,n,1) ans -= ht[i];
rep(i,1,n,1) ht[i] = rk[i] = sa[i] = tp[i] = 0;
write(ans,'\n');
}
}
signed main(){
solve();
}
正解是预处理,二维前缀和。
记\(ans_{l,r}\)表示区间\([l,r]\)及其子区间本质不同的字符串的个数。可以先维护差分数组然后前缀和解决。
先枚举子串长度,再枚举左端点,通过预处理的前缀和求出这\([l,r]\)一子串的哈希值,那么该串的左右端
点\([l,r]\)的答案加一,即\(ans_{l,r}++\),为了避免重复统计,该串还在上次该串出现位置到该位置
之间该串都有贡献,那么应该将上一次出现的左端点\(pre\)(字符串哈希维护)与当前右端点的答案减一,
即\(ans_{pre,r}--\),最后对数组求和得到的其实是会有重复贡献,要通过容斥计算出每个区间的答案\(ans_{l,r}\)
,回答询问
点此查看代码
#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)
#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 LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("substring.in"),*OutFile = outfile("substring.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 3010;
char s[N];
int n,m,ans[N][N];
ull hs[N],pw[N],base = 131;
inline ull get_hash(int l,int r){return hs[r] - hs[l-1]*pw[r-l+1];}
inline void solve(){
cin>>n>>m>>(s+1);
pw[0] = 1;rep(i,1,n,1) pw[i] = pw[i - 1] * base;
rep(i,1,n,1) hs[i] = hs[i-1]*base + s[i];
rep(len,1,n,1){
__gnu_pbds::gp_hash_table<ull,int> mp;
rep(l,1,n-len+1,1){
int r = l + len - 1;
ull h = get_hash(l,r);
ans[mp[h]][r]--;
mp[h] = l;
}
}
drep(l,n,1,1) rep(r,l,n,1)
ans[l][r] += ans[l + 1][r] + ans[l][r - 1] - ans[l+1][r-1] + 1;
while(m--){
int l,r;cin>>l>>r;cout<<ans[l][r]<<'\n';
}
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
魔法咒语
考虑一个串是被两个拼起来的或者就一个,一个的特判掉。
考虑对于每一个前缀扩展,假设拓展的第一个字符为\(c\)
加入\(c\)已经被扩展过了,那么看看它是否是最后一个节点,如果是,那么说明这个咒语本身就存在,直接加上一个即可。
反之,那么就考虑所有第一个字符为\(c\)的不同后缀都可以。
前缀的话插入一个正向的Trie就可以,看后缀的话插入一个后缀的Trie即可。
点此查看代码
#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)
#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 LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("magic.in"),*OutFile = outfile("magic.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
const int N = 4e5 + 10;
struct Trie{
int tree[N][27],cnt[N],tot;
inline void insert(string s,bool rev){
int p = 0,len = s.size()-1;
if(rev) reverse(s.begin(),s.end());
rep(i,0,len,1){
int x = s[i] - 'a' + 1;
if(!tree[p][x]) tree[p][x] = ++tot,cnt[x] += rev;
p = tree[p][x];
}
}
}t1,t2;
ll ans = 0,n;
string s[N];
bitset<27> ok,flag;
inline void solve(){
cin>>n;
rep(i,1,n,1){
cin>>s[i];
t1.insert(s[i],false);t2.insert(s[i],true);
int len = s[i].size();
ok[s[i].back() - 'a' + 1] = true;
if(len == 1 && !flag[s[i][0] - 'a' + 1]) ans++,flag[s[i][0] - 'a' + 1] = true;
}
rep(i,1,t1.tot,1) rep(j,1,26,1)
if(!t1.tree[i][j]) ans += t2.cnt[j];
else if(ok[j]) ans++;
cout<<ans<<'\n';
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}
表达式
线段树。不太懂啊。
将给的模数分解成\(\prod p_i^{k_i}\),然后以每个\(p_i^{k_i}\)为模数,求出答案以后再用CRT合并。
至于以\(p_i^{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)
#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 LOCAL
FILE *InFile = infile("in.in"),*OutFile = outfile("out.out");
// FILE *ErrFile=errfile("err.err");
#else
FILE *Infile = infile("expr.in"),*OutFile = outfile("expr.out");
//FILE *ErrFile = stderr;
#endif
using ll=long long;using ull=unsigned long long;
using db = double;using ldb = long double;
#define fi first
#define se second
#define pii pair<char,int>
#define mk make_pair
const int N = 2e5 + 10;
int n,m,mod;
pii a[N];
struct Segment_Tree{
struct segment_tree{
int l,r,val[30];
#define l(k) tree[k].l
#define r(k) tree[k].r
#define val(k) tree[k].val
}tree[N<<2];
int p;
unordered_map<char,function<int(int,int)> > op =
{
{'+',[&](int a,int b){return (a+b)%p;}},
{'*',[&](int a,int b){return 1ll*a*b%p;}},
{'^',[&](int a,int b){int res = 1;for(;b;b >>= 1,a = 1ll*a*a%p) if(b&1) res = 1ll*res*a%p;return res;}}
};
inline void pushup(int k){
rep(i,0,p-1,1) val(k)[i] = val(k<<1|1)[val(k<<1)[i]];
}
void build(int k,int l = 1,int r = n){
l(k) = l,r(k) = r;
if(l == r){
rep(i,0,p-1,1) val(k)[i] = op[a[l].fi](i,a[l].se);
return;
}
int mid = (l + r) >> 1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
pushup(k);
}
void change(int k,int pos){
if(l(k) == r(k)){
rep(i,0,p-1,1) val(k)[i] = op[a[pos].fi](i,a[pos].se);
return;
}
int mid = (l(k) + r(k)) >> 1;
if(pos <= mid) change(k<<1,pos);else change(k<<1|1,pos);
pushup(k);
}
}T[4];
int exgcd(int a,int b,int &x,int &y){
if(!b) return x = 1,y = 0,a;
int d = exgcd(b,a%b,x,y);
int t = x;x = y;y = t - (a/b)*y;
return d;
}
namespace TP{
const int N = 2e5 + 10;
char op[N];
int testid,n,m,mod,a[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 void solve(){
cin>>n>>m>>mod;
rep(i,1,n,1) cin>>op[i]>>a[i];
while(m--){
int p,x;cin>>p;
if(p == 1){
cin>>x;
rep(i,1,n,1){
if(op[i] == '+') x = (0ll+x+a[i])%mod;
if(op[i] == '*') x = 1ll*x*a[i]%mod;
if(op[i] == '^') x = power(x,a[i],mod);
}
cout<<x<<'\n';
}
else{
char c;int y;
cin>>x>>c>>y;op[x] = c,a[x] = y;
}
}
}
}
inline void solve(){
int id;
cin>>id;
if(id <= 3){
TP::solve();
return;
}
cin>>n>>m>>mod;
rep(i,1,n,1) cin>>a[i].fi>>a[i].se;
vector<int> d;
int res = mod;
for(int i = 2;i * i <= res; ++i){
if(res % i == 0){
int k = 1;
while(res % i == 0) res /= i,k *= i;
d.emplace_back(k);
}
}
if(res > 1) d.emplace_back(res);
for(int i = 0;i < d.size(); ++i){
T[i].p = d[i],T[i].build(1);
}
// exit(0);
auto get_ans = [&](vector<int> &v){
int res = 0;
for(int i = 0;i < v.size(); ++i){
int m = mod/d[i],b,y;
exgcd(m,d[i],b,y);
res = (res + 1ll * v[i] * m % mod * b % mod) % mod;
}
return (res + mod) % mod;
};
while(m--){
int op;cin>>op;
if(op == 1){
int x;cin>>x;
vector<int> v;
for(int i = 0;i < d.size(); ++i) v.emplace_back(T[i].tree[1].val[x%T[i].p]);
cout<<get_ans(v)<<'\n';
}
else{
int x,z;cin>>x;char y;cin>>y>>z;
a[x] = mk(y,z);
for(int i = 0;i < d.size();T[i++].change(1,x));
}
}
}
signed main(){
cin.tie(nullptr)->sync_with_stdio(false);
cout.tie(nullptr)->sync_with_stdio(false);
solve();
}