• 某古 9 月月赛 I 游记


    由于博客已经估了好久没写了,所以跑来更篇博客。

    第一次 AK div2 ,莫名感动。

    A [Cnoi2020]子弦

    题目分析

    这题难道有比 SAM 还简单的方法???

    越短的串出现次数才可能会越多,直接统计每个字符出现的次数,输出最大值即可。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=10000007;
    char s[maxn];int cnt[26];
    int main(){
    	scanf("%s",s);int len=strlen(s),ans=0;
    	for(int i=0;i<len;++i)++cnt[s[i]-'a'];
    	for(int i=0;i<26;++i)ans=max(ans,cnt[i]);
    	write(ans),pc('\n');
    	return 0;
    }
    
    

    B [Cnoi2020]雷雨

    题目分析

    一条路径肯定是从雷云出发然后从某处分界直到底部,所以我们可以求出任意点到三个点之间的最短路,然后枚举从哪里分界即可。

    求最短路时可以不用建出图,直接枚举上下左右就可以了。

    我一开始做的时候就直接放弃这种想法了,感觉这个复杂度有点大,但是还是可以跑过。

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=1005;
    struct Node{
    	int nx,ny;long long dis;
    	Node(int nx=0,int ny=0,long long dis=0):
    		nx(nx),ny(ny),dis(dis){}
    	bool operator < (const Node o)const{
    		return dis<o.dis;
    	}
    }heap[maxn*maxn*4];
    int sz;
    void push(Node o){
    	heap[++sz]=o;int no=sz;
    	while(no>1&&o<heap[no>>1])
    		heap[no]=heap[no>>1],no>>=1;
    	heap[no]=o;
    }
    void pop(void){
    	swap(heap[1],heap[sz--]);int no=1;
    	while("Imakf ak ioi"){
    		int nt=no;
    		if((no<<1)<=sz&&heap[no<<1]<heap[nt])nt=no<<1;
    		if((no<<1|1)<=sz&&heap[no<<1|1]<heap[nt])nt=no<<1|1;
    		if(no==nt)break;swap(heap[no],heap[nt]);no=nt;
    	}
    }
    Node top(void){
    	return heap[1];
    }
    int R[maxn][maxn],vis[maxn][maxn];
    long long dis[3][maxn][maxn];
    const int dx[4]={0,1,0,-1},
    		  dy[4]={1,0,-1,0};
    int n,m;
    void solve(int o,int sx,int sy){
    	memset(dis[o],0x3f,sizeof dis[o]);
    	memset(vis,0,sizeof vis);
    	push(Node(sx,sy,dis[o][sx][sy]=R[sx][sy]));
    	while(sz){
    		Node tmp=top();int nx=tmp.nx,ny=tmp.ny;pop();
    		if(vis[nx][ny])continue;vis[nx][ny]=true;
    		for(int d=0;d<4;++d){
    			int tx=nx+dx[d],ty=ny+dy[d];
    			if(tx<1||tx>n||ty<1||ty>m)continue;
    			long long wi=dis[o][nx][ny]+R[tx][ty];
    			if(dis[o][tx][ty]<=wi)continue;
    			push(Node(tx,ty,dis[o][tx][ty]=wi));
    		}
    	}
    }
    int main(){
    	int a,b,c;
    	read(n),read(m),read(a),read(b),read(c);
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			read(R[i][j]);
    	solve(0,1,a);solve(1,n,b);solve(2,n,c);
    	long long ans=0x3f3f3f3f3f3f3f3fll;
    	for(int i=1;i<=n;++i)
    		for(int j=1;j<=m;++j)
    			ans=min(ans,dis[0][i][j]+dis[1][i][j]+dis[2][i][j]-R[i][j]*2);
    	write(ans),pc('\n');
    	return 0;
    }
    
    

    C [Cnoi2020]梦原

    题目分析

    艹,怎么有人出和我一样的屑题啊。

    考虑加入一个点会造成什么样的贡献,如果它的 \(a\) 比它父亲的 \(a\) 值要小,那么在减小它父亲的 \(a\) 的时候就可以顺便减小它自己的 \(a\) ,否则就会增加它们两个差的使用次数。

    \(i\) 的父亲可以选择的集合为 \(S\) ,那么 \(i\) 对答案造成的贡献就是:

    \[\dfrac{1}{\operatorname{size}(S)}\sum_{x\in S}\max(0,a_i-a_x)=\dfrac{1}{\operatorname{size}(S)}\sum_{x\in S}(a_i-a_x)[a_i\ge a_x] \]

    相当于就是要求 \([i-k,i-1]\cap N^+\)\(a\) 值比 \(a_i\) 小的数的个数和这些数 \(a\) 值的和,树套树可能过不了,离线后可以使用树状数组差分求答案。

    时间复杂度: \(\mathcal O(n\log_2n)\)

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=1000006,mod=998244353;
    int mo(const int x){
    	return x>=mod?x-mod:x;
    }
    int rt[maxn],tr[maxn],cnt;
    void change(int pos,int val){
    	while(pos<=cnt){
    		tr[pos]=mo(tr[pos]+val);
    		rt[pos]=mo(rt[pos]+1);
    		pos+=pos&(-pos);
    	}
    }
    int RT;
    int query(int pos){
    	int re=0;RT=0;
    	while(pos){
    		re=mo(re+tr[pos]);
    		RT=mo(RT+rt[pos]);
    		pos^=pos&(-pos);
    	}
    	return re;
    }
    struct Edge{
    	int v,id,nt;
    	Edge(int v=0,int id=0,int nt=0):
    		v(v),id(id),nt(nt){}
    }e[maxn*2];
    int hd[maxn],num;
    void qwq(int u,int v,int id){
    	e[++num]=Edge(v,id,hd[u]),hd[u]=num;
    }
    int a[maxn],A[maxn],inv[maxn],sum[maxn],mus[maxn];
    int main(){
    	int n,k,ans=0;read(n),read(k);
    	inv[1]=1;
    	for(int i=2;i<=n;++i)
    		inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    	for(int i=1;i<=n;++i)read(a[i]),A[i-1]=a[i];ans=a[1];
    	sort(A,A+n);cnt=unique(A,A+n)-A;
    	for(int i=1;i<=n;++i)a[i]=lower_bound(A,A+cnt,a[i])-A+1;
    	for(int i=2;i<=n;++i){
    		int r=i-1,l=max(1,i-k);
    		qwq(l-1,a[i],-i);
    		qwq(r,a[i],i);
    	}
    	for(int i=1;i<=n;++i){
    		change(a[i],A[a[i]-1]);
    		for(int j=hd[i];j;j=e[j].nt){
    			int v=e[j].v,id=e[j].id,tmp=query(v);
    			if(id>0)sum[id]=mo(sum[id]+tmp),mus[id]=mo(mus[id]+RT);
    			else sum[-id]=mo(mod-tmp+sum[-id]),mus[-id]=mo(mus[-id]-RT);
    		}
    	}
    	for(int i=2;i<=n;++i){
    		int r=i-1,l=max(1,i-k),len=r-l+1;
    		ans=mo(1ll*mo(mod-sum[i]+1ll*A[a[i]-1]*mus[i]%mod)*inv[len]%mod+ans);
    	}
    	write(ans),pc('\n');
    	return 0;
    }
    
    

    D [Cnoi2020]线形生物

    题目分析

    \(E_i\) 表示从 \(i\) 走到 \(n+1\) 的期望步数,设 \(i\) 的到达集合为 \(S\) ,那么有:

    \[E_i=1+\dfrac{1}{\operatorname{size}(S)}\sum_{j\in S}E_j \]

    不妨设 \(E_i=A_iE_1+B_i\) ,那么显然可以从 \(A_1,B_1\) 一直推出 \(A_{n+1},B_{n+1}\) ,那么 \(A_{n+1}E_1+B_{n+1}=E_{n+1}=0\) ,所以 \(E_1=-\dfrac{B_{n+1}}{A_{n+1}}\)

    具体如何推,不妨设 \(i\) 通过返祖边到达的集合为 \(S^{'}\) ,那么:

    \[E_i=1+\dfrac{1}{\operatorname{size}(S^{'})+1}(E_{i+1}+\sum_{j\in S^{'}}E_j) \]

    \[E_{i+1}=(\operatorname{size}(S^{'})+1)(E_i-1)-\sum_{j\in S^{'}}E_j \]

    \[E_{i+1}=E_1\times (A_i(\operatorname{size}(S^{'})+1)-\sum_{j\in S^{'}}A_j)+(\operatorname{size}(S^{'})+1)(B_i-1)-\sum_{j\in S^{'}}B_j \]

    \[A_{i+1}=A_i(\operatorname{size}(S^{'})+1)-\sum_{j\in S^{'}}A_j \]

    \[B_{i+1}=(\operatorname{size}(S^{'})+1)(B_i-1)-\sum_{j\in S^{'}}B_j \]

    参考代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    #define ch() getchar()
    #define pc(x) putchar(x)
    template<typename T>inline void read(T&x){
    	int f;char c;
    	for(f=1,c=ch();c<'0'||c>'9';c=ch())if(c=='-')f=-f;
    	for(x=0;c<='9'&&c>='0';c=ch())x=x*10+(c&15);x*=f;
    }
    template<typename T>inline void write(T x){
    	static char q[64];int cnt=0;
    	if(!x)pc('0');if(x<0)pc('-'),x=-x;
    	while(x)q[cnt++]=x%10+'0',x/=10;
    	while(cnt--)pc(q[cnt]);
    }
    const int maxn=1000005,maxm=1000005,mod=998244353;
    int mo(const int x){return x>=mod?x-mod:x;}
    int power(int a,int x){
    	int re=1;
    	while(x){
    		if(x&1)re=1ll*re*a%mod;
    		a=1ll*a*a%mod,x>>=1;
    	}
    	return re;
    }
    int d[maxn];
    struct Edge{
    	int v,nt;
    	Edge(int v=0,int nt=0):
    		v(v),nt(nt){}
    }e[maxm];
    int hd[maxn],num,A[maxn],B[maxn];
    void qwq(int u,int v){
    	e[++num]=Edge(v,hd[u]),hd[u]=num;
    }
    int main(){
    	int id,n,m;
    	read(id),read(n),read(m);
    	for(int i=1;i<=m;++i){
    		int u,v;read(u),read(v);
    		++d[u];qwq(u,v);
    	}
    	A[1]=1,B[1]=0;
    	for(int i=1;i<=n;++i){
    		int sA=0,sB=0;
    		for(int j=hd[i];j;j=e[j].nt){
    			int v=e[j].v;
    			sA=mo(sA+A[v]);
    			sB=mo(sB+B[v]);
    		}
    		A[i+1]=mo(mod-sA+1ll*(d[i]+1)*A[i]%mod);
    		B[i+1]=mo(mod-mo(d[i]+1+sB)+1ll*(d[i]+1)*B[i]%mod);
    	}
    	write(1ll*mo(mod-B[n+1])*power(A[n+1],mod-2)%mod),pc('\n');
    	return 0;
    }
    
    

    小结

    噫!好!我 AK 了!

    这次的月赛还是很简单的,算是良心赛,可以很好地考察一些选手地卡常技巧(大雾。

    可能说明我搞了许久 OI 还是有进步的?但愿如此。

  • 相关阅读:
    lambda函数
    linux 自学系列:wc命令
    linux 自学系列:chmod 权限操作
    linux 自学系列:创建、删除目录、移动、更名文件或目录
    linux 自学系列:vi、vim编辑工具
    《架构之美》学习随笔:设计第一步
    安装memcache 时提示error while loading shared libraries: libevent2.0解决办法
    《架构之美》学习随笔:保证质量
    linux 自学系列:环境变量设置
    logging模块学习笔记:logger 对象、日志等级
  • 原文地址:https://www.cnblogs.com/lsq147/p/13697104.html
Copyright © 2020-2023  润新知