【做题日记】
P3294 [SCOI2016]背单词
贪心+Trie+dfs
贪心:对于一个串和他的前缀子串,尽可能把前缀放在前面
把后缀反向插转化为为前缀 trie插一遍
并查集重构树dfs求子树大小
按规则求和
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
const int N=500057;
int n,idx,val[N],tot,size[N],fa[N],id[N];
long long ans;
int ch[N][30];
char s[N];
vector<int>tree[N];
inline int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
inline void insert(char s[],int num)
{
int p=0;
for(int i=strlen(s)-1;i>=0;i--)
{
int u=s[i]-'a';
if(!ch[p][u]) ch[p][u]=++idx;
p=ch[p][u];
}
val[p]=num;
}
inline void remake(int now)
{
for(int i=0;i<26;i++)
{
int ver=ch[now][i];
if(!ver) continue;
if(!val[ver])
{
fa[ver]=find(now);
}
else
{
tree[val[find(now)]].push_back(val[ver]);
}
remake(ver);
}
}
bool cmp(int x,int y)
{
return size[x]<size[y];
}
inline void make(int now)
{
size[now]=1;
for(int i=0;i<tree[now].size();i++)
{
make(tree[now][i]);
size[now]+=size[tree[now][i]];
}
sort(tree[now].begin(),tree[now].end(),cmp);
}
inline void dfs(int now)
{
id[now]=tot++;
for(int i=0;i<tree[now].size();i++)
{
ans+=tot-id[now];
dfs(tree[now][i]);
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%s",s);
insert(s,i);
}
for(int i=1;i<=idx;i++) fa[i]=i;
remake(0);
make(0);
dfs(0);
printf("%lld",ans);
return 0;
}
P3065 [USACO12DEC]First! G
Trie+拓扑
对于每一个字符串,能不能通过改变字母表,来让其排在最前面。
存在前缀相同的串或者其他字符串,则建边跑topo,判断有没有环,有环则无解。
已经有前缀存在,直接跳过。
Code
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
const int N=300007;
queue<int>q;
int val[N],ch[N][30],f,h[30],e[1000],ne[1001],ans[N],idxx,idx,in[30],n,cnt;
char s[30010][100];
bool vis[30][30];
inline void add(int a,int b)
{
e[idxx]=b,ne[idxx]=h[a],h[a]=idxx++;
}
inline void insert(char s[])
{
int p=0;
for(int i=0;i<strlen(s);i++)
{
int u=s[i]-'a';
if(!ch[p][u]) ch[p][u]=++idx;
p=ch[p][u];
}
val[p]=1;
}
inline bool topo()
{
while(q.size()) q.pop();
for(int i=0;i<26;i++)
{
if(!in[i]) q.push(i);
}
while(q.size())
{
int t=q.front();q.pop();
for(int i=h[t];~i;i=ne[i])
{
int ver=e[i];
--in[ver];
if(!in[ver])
{
q.push(ver);
}
}
}
for(int i=0;i<26;i++)
{
if(in[i])
{
return 0;
}
}
return 1;
}
inline void solve(char s[])
{
int p=0;
for(int i=0;i<strlen(s);i++)
{
int u=s[i]-'a';
if(val[p])
{
f=1;
return ;
}
for(int j=0;j<26;j++)
{
if(ch[p][j]&&j!=u&&!vis[u][j])
{
vis[u][j]=1;
add(u,j);
in[j]++;
}
}
p=ch[p][u];
}
}
inline void init()
{
idxx=0;
f=0;
memset(h,-1,sizeof h);
memset(ne,0,sizeof ne);
memset(vis,0,sizeof vis);
memset(in,0,sizeof in);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]);
insert(s[i]);
}
for(int i=1;i<=n;i++)
{
init();
solve(s[i]);
if(f) continue;
if(topo()) ans[++cnt]=i;
}
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++)
{
printf("%s\n",s[ans[i]]);
}
return 0;
}
2022桂林M题
逆序对
2022四川省赛E题
枚举a,b min25筛求素数前缀和
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod=998244353;
ll inv2=499122177ll;
ll inv4=748683265ll;
ll inv6=166374059ll;
const ll N=1e6;
ll ksm(ll t, ll k)
{
ll res=1%mod;
while (k)
{
if (k&1) res=res*t%mod;
t=t*t%mod;
k>>=1;
}
return res%mod;
}
ll cnt,n;
ll pre0[N+10],pre1[N+10],pre2[N+10],pre3[N+10];
ll prime[N+10];
bool vis[N+10];
inline void init()
{
for(int i=2;i<=N;i++)
{
if(!vis[i])
{
prime[++cnt]=i;
pre0[cnt]=(pre0[cnt-1]+1ll)%mod;
pre1[cnt]=(pre1[cnt-1]+1ll*i%mod)%mod;
pre2[cnt]=(pre2[cnt-1]+1ll*i*i%mod)%mod;
pre3[cnt]=(pre3[cnt-1]+1ll*i*i%mod*i%mod)%mod;
}
for(int j=1;i*prime[j]<=N&&j<=cnt;j++)
{
vis[i*prime[j]]=true;
if(i%prime[j]==0) break;
}
}
}
// 前n项自然幂数和
inline ll sum1(ll x)
{
x%=mod;
return (1ll+x)*x%mod*inv2%mod;
}
inline ll sum2(ll x)
{
x%=mod;
return (x*(x+1ll)%mod*(2ll*x%mod+1ll)%mod)%mod*inv6%mod;
}
inline ll sum3(ll x)
{
x%=mod;
return (x*x%mod*(x+1ll)%mod)%mod*(x+1ll)%mod*inv4%mod;
}
ll g0[N+10],g1[N+10],g2[N+10],g3[N+10];
ll w[N+10],tot,id1[N+10],id2[N+10];
void solve()
{
init();
ll sq=sqrt(n+0.5);
for(ll l=1,r;l<=n;l=r+1)
{
r=n/(n/l);
w[++tot]=n/l;
ll tmp=(n/l)%mod;
g0[tot]=(tmp-1ll+mod)%mod;
g1[tot]=(sum1(tmp)-1ll+mod)%mod;
g2[tot]=(sum2(tmp)-1ll+mod)%mod;
g3[tot]=(sum3(tmp)-1ll+mod)%mod;
if(tmp<=sq) id1[n/l]=tot;
else id2[r]=tot;
}
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=tot&&prime[i]*prime[i]<=w[j];j++)
{
// 定位k
int k;
if(w[j]/prime[i]<=sq) k=id1[w[j]/prime[i]];
else k=id2[n/(w[j]/prime[i])];
// g(n)=g(n)-h(i)*(g(k)-pre[i-1])
g0[j]=(g0[j]-(g0[k]-pre0[i-1]+mod)%mod+mod)%mod;
g1[j]=(g1[j]-prime[i]*(g1[k]-pre1[i-1]+mod)%mod+mod)%mod;
g2[j]=(g2[j]-prime[i]*prime[i]%mod*(g2[k]-pre2[i-1]+mod)%mod+mod)%mod;
g3[j]=(g3[j]-(prime[i]*prime[i]%mod*prime[i])%mod*((g3[k]-pre3[i-1]+mod)%mod+mod))%mod;
}
}
for(int i=1;i<=tot;i++)
{
//cout<<i<<" "<<g0[i]<<endl;
}
ll ans=0;
for(int i=1;i<=cnt&&prime[i]*prime[i]*prime[i]<=n;i++)
{
for(int j=i+1;j<=cnt&&n/prime[i]/prime[j]>prime[j];j++)
{
// a=p1+p2 b=p3
// (a+b)^3=a^3+b^3+3(a^2)b+3a(b^2);
ll a=(prime[i]+prime[j])%mod;
ll a2=ksm(a,2);
ll a3=ksm(a,3);
ll b=n/prime[i]/prime[j];
// 定位k
int k;
if(b<=sq) k=id1[b];
else k=id2[n/b];
ans=(ans+(a3*(g0[k]-pre0[j]+mod)%mod)%mod)%mod;
ans=(ans+(g3[k]-pre3[j]+mod)%mod)%mod;
ans=(ans+(3ll*a2%mod*(g1[k]-pre1[j]+mod)%mod)%mod)%mod;
ans=(ans+(3ll*a%mod*(g2[k]-pre2[j]+mod)%mod)%mod)%mod;
}
}
printf("%lld",ans);
}
int main()
{
scanf("%lld",&n);
solve();
return 0;
}