赛中通过
B. Fire-Fighting Hero(最短路/多源bfs 签到)
题意
比较多源bfs的最短路和单源最短路哪个大
题解
可以对多源建个虚点,跑两次单源最短路
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define pb push_back
#define fi first
#define se second
typedef pair<int,int> P;
const int INF=0x3f3f3f3f;
const int N=1e3+5;
vector<P>E[N];
vector<int>now,hero;
int d[N],ans1,ans2;
int t,n,m,s,k,c,u,v,w;
int bfs(vector<int> &now)
{
int res=0;
memset(d,INF,sizeof d);
priority_queue<P,vector<P>,greater<P> >q;
for(int i=0;i<now.size();++i)
{
int v=now[i];
d[v]=0;q.push(P(0,v));
}
while(!q.empty())
{
int dis=q.top().first,u=q.top().second;
q.pop();
if(d[u]<dis)continue;
int len=E[u].size();
for(int i=0;i<len;++i)
{
int v=E[u][i].fi;
int w=E[u][i].se;
if(d[v]>d[u]+w)
{
d[v]=d[u]+w;
q.push(P(d[v],v));
}
}
}
for(int i=1;i<=n;++i)
res=max(res,d[i]);
return res;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&n,&m,&s,&k,&c);
for(int i=1;i<=n;++i)
E[i].clear();
hero.clear();
hero.pb(s);
now.clear();
for(int i=1;i<=k;++i)
{
scanf("%d",&v);
now.pb(v);
}
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&u,&v,&w);
E[u].pb(P(v,w));
E[v].pb(P(u,w));
}
ans1=bfs(hero);
ans2=bfs(now);
if(1ll*ans1<=1ll*ans2*c)printf("%d\n",ans1);
else printf("%d\n",ans2);
}
return 0;
}
E. Magic Master(约瑟夫环模拟)
约瑟夫环模拟复杂度O(T*n*m),n为圈长,m为每次跳的步数,常数小所以能过
#include<bits/stdc++.h>
using namespace std;
const int N=4e7+10;
int t,n,m,q;
int nex[N],ans[N],cnt;
int now,tmp,k;
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
//0到n-1
cnt=0;now=1;
for(int i=1;i<n;++i)nex[i]=i+1;
nex[n]=1;
ans[now]=++cnt;
nex[n]=2;
for(int j=1;j<n;++j)
{
for(int i=1;i<=m;++i)
now=nex[now];
tmp=nex[now];
ans[tmp]=++cnt;
nex[now]=nex[tmp];
}
scanf("%d",&q);
for(int j=1;j<=q;++j)
{
scanf("%d",&k);
printf("%d\n",ans[k]);
}
}
return 0;
}
G.过于签到 不配拥有姓名
赛后补题
C.Hello 2019(线段树维护转移矩阵)
题意
长度为n(n<=2e5)的数字串,q(q<=2e5)个询问,
每次询问区间[l,r]内,最少删去多少个字符,
使得区间含9102子序列且不含8102子序列,
不能实现输出-1
题解
goodbye2016原题,就改了一下顺序,
先把串反过来,等价于含2019且不含2018子序列,
线段树,每个位置维护一个5*5矩阵,代表5种状态
0状态:空串 1状态:子序列2 2状态:子序列20
3状态:子序列201 4状态:子序列2019
矩阵中c[i][j]定义为从i状态转移到j状态所要删去的最少的字符数
初始时,c[i][j]=INF(i!=j),c[i][j]=0(i==j),
每加入一个字符p使得i能转移到i+1状态时,
c[i][i+1]=0,c[i][i]=1(此时想要保持原状态不变只能删了p)
特别地,当加入8进来时,
201无法再保持201,2019也无法保持2019(因为必后缀8使得产生子序列2018,必删8),
所以,有c[3][3]=c[4][4]=1,
重载矩阵乘法,通过枚举k,维护从i状态到j状态转移删去字符最小值
代码
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
const int INF=0x3f3f3f3f;
int n,q,l,r,v;
char a[N];
struct mat
{
const static int MAXN = 5;
int c[MAXN][MAXN];
int m, n;
mat(){
memset(c,0,sizeof (c));
}
mat(int a, int b) : m(a), n(b) {
memset(c, 0, sizeof (c));
}
void reset(int a,int b){
m=a; n=b;
}
void clear(){
memset(c, 0, sizeof(c));
}
mat operator + (const mat& temp) {
mat ans(m, temp.n);
for (int i = 0; i < m; i ++)
for (int j = 0; j < temp.n; j ++)
{
ans.c[i][j] = INF;
for (int k = 0; k < n; k ++)
ans.c[i][j] = min(ans.c[i][j] , c[i][k] + temp.c[k][j]);
}
return ans;
}
}dat[N*4],ans;
void pushup(int p)
{
dat[p]=dat[p<<1]+dat[p<<1|1];
}
void init(int p,int l,int r)
{
for(int i=0;i<dat[p].n;++i)
{
for(int j=0;j<dat[p].m;++j)
{
dat[p].c[i][j]=(i==j)?0:INF;
}
}
if(a[l]=='2')dat[p].c[0][1]=0,dat[p].c[0][0]=1;//空 后者1说明不转不行 不转必须删
if(a[l]=='0')dat[p].c[1][2]=0,dat[p].c[1][1]=1;//2
if(a[l]=='1')dat[p].c[2][3]=0,dat[p].c[2][2]=1;//20
if(a[l]=='9')dat[p].c[3][4]=0,dat[p].c[3][3]=1;//201
if(a[l]=='8')dat[p].c[3][3]=1,dat[p].c[4][4]=1;//2019 201->201必删此8
}
void build(int p,int l,int r)//每一行进行操作
{
dat[p].reset(5,5);
if(l==r)
{
init(p,l,r);
return;
}
int mid=(l+r)/2;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
pushup(p);
}
mat ask(int p,int l,int r,int ql,int qr)
{
if(ql<=l&&r<=qr)return dat[p];
int mid=(l+r)/2;
if(ql>mid)return ask(p<<1|1,mid+1,r,ql,qr);
if(qr<=mid)return ask(p<<1,l,mid,ql,qr);
return ask(p<<1,l,mid,ql,mid)+ask(p<<1|1,mid+1,r,mid+1,qr);
}
int main()
{
scanf("%d%d",&n,&q);
scanf("%s",a+1);
reverse(a+1,a+n+1);
build(1,1,n);
while(q--)
{
scanf("%d%d",&l,&r);
l=n+1-l;r=n+1-r;swap(l,r);
ans=ask(1,1,n,l,r);
v=ans.c[0][4];
printf("%d\n",v==INF?-1:v);
}
return 0;
}
D.Interesting Series(母函数+分治FFT)
题目
n,q<=1e5,2<=a<=1e3,1<=si<=1e9
F(1)=1,F(n)=a*F(n-1)+1,
n个数,第i个数为si,
一个集合s的价值v(s),定义为F(sum),其中sum为该集合内所有元素和
对每个子集内元素个数相同的子集,将其价值再求和,作为该元素个数下的答案
q次询问,每次询问一个k,输出对于该子集元素个数k下的答案%(1e6+3)
题解
考虑F(n),写成等比数列求和形式,
构造生成函数如下,
对于每个括号内都二选一,对于选到了一些的集合,
不妨计集合内元素个数为k个,和为
其在生成函数中的乘积可表示为,一个集合的贡献应为
分治FFT求出的系数,共有C(n,k)个集合对其起到贡献,
故对于一个询问k,其答案为
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=1<<18,mod=1e5+3;
double PI=acos(-1.0);
int n,a,q,k;
int c[N],s[N];
struct C{
double r,i;
C(){}
C(double a,double b){r=a,i=b;}
C operator + (C x){return C(r+x.r,i+x.i);}
C operator - (C x){return C(r-x.r,i-x.i);}
C operator * (C x){return C(r*x.r-i*x.i,r*x.i+i*x.r);}
}w[N],A[N],B[N];
int R[N];
vector <int> colors[N<<1];
struct cmp{
bool operator ()(int a,int b){
return colors[a].size()>colors[b].size();
}
};
priority_queue <int,vector<int>,cmp> heap;
void FFT(C a[],int n){
for (int i=0;i<n;i++)
if (i<R[i])
swap(a[i],a[R[i]]);
for (int t=n>>1,d=1;d<n;d<<=1,t>>=1)
for (int i=0;i<n;i+=(d<<1))
for (int j=0;j<d;j++){
C tmp=w[t*j]*a[i+j+d];
a[i+j+d]=a[i+j]-tmp;
a[i+j]=a[i+j]+tmp;
}
}
void FFT_times(vector <int> &a,vector <int> &b,vector <int> &c){
int n,d;
for (int i=0;i<a.size();i++)
A[i]=C(a[i],0);
for (int i=0;i<b.size();i++)
B[i]=C(b[i],0);
for (n=1,d=0;n<a.size()+b.size()-1;n<<=1,d++);
for (int i=0;i<n;i++){
R[i]=(R[i>>1]>>1)|((i&1)<<(d-1));
w[i]=C(cos(2*PI*i/n),sin(2*PI*i/n));
}
for (int i=a.size();i<n;i++)
A[i]=C(0,0);
for (int i=b.size();i<n;i++)
B[i]=C(0,0);
FFT(A,n),FFT(B,n);
for (int i=0;i<n;i++)
A[i]=A[i]*B[i],w[i].i*=-1.0;
FFT(A,n);
c.clear();
for (int i=0;i<=a.size()+b.size()-2;i++)
c.push_back(((LL)(A[i].r/n+0.5))%mod);
}
int modpow(int x,int n,int mod)
{
int res=1;
for(;n;n>>=1,x=1ll*x*x%mod)
if(n&1)res=1ll*res*x%mod;
return res;
}
int main(){
scanf("%d%d%d",&n,&a,&q);
for(int i=1;i<=n;++i)
scanf("%d",&s[i]);
while (!heap.empty())
heap.pop();
int sz=0;
for (int i=1;i<=n;i++){
colors[++sz].clear();
colors[sz].push_back(modpow(a,s[i],mod));
colors[sz].push_back(1);
heap.push(sz);
}
while (heap.size()>=2){
int x=heap.top();
heap.pop();
int y=heap.top();
heap.pop();
FFT_times(colors[x],colors[y],colors[++sz]);
colors[x].clear(),colors[y].clear();
heap.push(sz);
}
c[0]=1;//C(n,0)
for(int i=1;i<=n;++i)
c[i]=1ll*c[i-1]*(n-i+1)%mod*modpow(i,mod-2,mod)%mod;
int inv=modpow(1-a+mod,mod-2,mod);
for(int i=1;i<=q;++i)
{
scanf("%d",&k);
int res=(c[k]-colors[sz][n-k]+mod)%mod;
res=1ll*res*inv%mod;
printf("%d\n",res);
}
return 0;
}
H.The Nth Item(分块优化矩阵块速幂)
有个很类似的题目 洛谷P5110 块速递推
题意
给定一个递推式f[0]=0,f[1]=1,f[i]=3*f[i-1]+2*f[i-2],
询问f[n](0<=n<=1e18)%998244353,强制在线,询问q(q<=1e7)次
题解
打表得,该式循环节cyc为(mod-1)/2,mod取998244353
所以,问题转化为询问一个f[n](0<=n<cyc)的值,n在1e9以内,
像bsgs一样打表,块内每个单独打,块外按块号整个打表,
块的大小,取sqrt(n)即可,空间开2*sqrt(n)即可存下
代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
const int inv2=(mod+1)/2;
const int cyc=(mod-1)/2;
const int m17=473844410;//sqrt(17) 二次剩余
const int im17=438914993;//1/sqrt(17) m17^(mod-2)
typedef long long ll;
int q,ans;
ll n,res;
struct POW
{
const static int N=1e5;
int v,high[N+2],low[N+2];
void init(int x)
{
v=x;
low[0]=1;
for(int i=1;i<=N;++i)
low[i]=(1ll*low[i-1]*v)%mod;
high[0]=1;
for(int i=1;i<=N;++i)
high[i]=(1ll*high[i-1]*low[N])%mod;
}
int solve(int x)
{
return 1ll*high[x/N]*low[x%N]%mod;
}
}a,b;
int main()
{
a.init(1ll*(3+m17)*inv2%mod);
b.init((1ll*(3-m17)*inv2%mod+mod)%mod);
scanf("%d%lld",&q,&n);
while(q--)
{
ans=1ll*(a.solve(n%cyc)-b.solve(n%cyc)+mod)*im17%mod;
res^=ans;
n=n^(1ll*ans*ans);
}
printf("%lld\n",res);
return 0;
}