• 题解-Codeforces1106全套


    因为参加完wc后心情很差,而且在广州过年没Ubuntu,所以就没打这场比赛了,结果这套题全部1A了,现在看来真是错失良机 结果这场不计rating

    今天是除夕,大家节日快乐

    A. Lunar New Year and Cross Counting

    题意

    给定 (n imes n)(01) 矩阵,定义一个十字为摆成X的五个 (1) ,问矩阵内部有多少这种十字((nleq 500)

    思路

    A题不出意外一般是直接模拟,复杂度(O(n^2))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const char ch='X';
    char s[503][503];
    int n,ans;
    
    int main(){
    	scanf("%d",&n);
    	for(int i=1;i<=n;++i)
    		scanf("%s",s[i]+1);
    	for(int i=2;i<n;++i)
    	for(int j=2;j<n;++j)
    		if(s[i][j]==ch && s[i-1][j-1]==ch && s[i-1][j+1]==ch && s[i+1][j-1]==ch && s[i+1][j+1]==ch)
    			++ans;
    	cout << ans << endl;
    	return 0;
    }
    

    B. Lunar New Year and Food Ordering

    题意

    给定(n)份食物,每份食物有自己的价格和数量,给定(m)个依次到达的客人,第(i)个人会要(d_i)个第(t_i)种食物,若不够,则从剩下食物中最便宜的选起,求每人的花费((n,mleq 10^5)

    思路

    感觉随便乱搞的题,关键在于如何快速找到剩下食物中最便宜的,只要将食物按价格升序排序后记录一个指针即可,在该指针前的食物全部售罄,复杂度(O(nlog n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <typename _tp> inline void read(_tp&x){
    	char c11=getchar(),ob=0;x=0;
    	while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
    }
    
    const int N=101003;
    int c[N],v[N],t[N];
    int n,m;
    
    inline bool cmp(const int&A,const int&B){return v[A]<v[B];}
    
    int main(){
    	read(n),read(m);
    	for(int i=1;i<=n;++i)read(c[i]);
    	for(int i=1;i<=n;++i)read(v[i]);
    	for(int i=1;i<=n;++i)t[i]=i;
    	sort(t+1,t+n+1,cmp);
    
    	int lst=1,ps,ct;
    	ll ans;
    	while(m--){
    		read(ps),read(ct);
    		if(c[ps]>=ct)c[ps]-=ct,ans=(ll)ct*v[ps];
    		else {
    			ans=(ll)c[ps]*v[ps];
    			ct-=c[ps],c[ps]=0;
    			while(lst<=n){
    				int id=t[lst];
    				if(c[id]>=ct){
    					c[id]-=ct;
    					ans+=(ll)ct*v[id];
    					ct=0;
    					break;
    				}ct-=c[id];
    				ans+=(ll)c[id]*v[id];
    				c[id]=0;
    				++lst;
    			}
    			if(ct)ans=0;
    		}
    		printf("%I64d
    ",ans);
    	}
    	return 0;
    }
    

    C. Lunar New Year and Number Division

    题意

    给定(n)个元素(保证(n)为偶数),将这些元素划分为若干个组,要求每个组元素个数不低于两个,不设上限,每个组的权值为组内元素和的平方,定义一种划分方案的权值为所有组贡献的和,求划分方案的最小值((nleq 3 imes 10^5)

    思路

    题目给了很好的提示,应该能猜想一定存在一种最优划分将元素划分为两两一组,想法证明:

    首先若存在一组超过四个元素的,定能将其划分为若干个更小的组,相应的,其贡献也会更低

    若存在一组三个元素的,由于(n)为偶数,则定能找到另一组三个元素,由于三个元素(a,b,c)的贡献为(a^2+b^2+c^2+2ab+2ac+2bc),由于其中平方项是无论如何划分都会产生的,所以有区别的只有(2ab+2ac+2bc),产生了三个附加项,两个组共六个附加项,若划分为两两一组,则只会产生三个附加项,再加分类讨论即可证明两两一组更优

    现在知道了两两一组最优,但如何划分?由于要尽量避免两个大元素相乘,所以第(i)小元素和第(i)大元素一组。想法证明:

    对四个元素的情况进行讨论:

    (aleq bleq cleq d),要证(ad,bc)一组最优

    即证(egin{cases} ab+cd-ad-bcgeq 0\ ac+bd-ad-bcgeq 0 end {cases})

    对于上面的式子:

    [ab+cd-ad-bc=(d-b)(c-a)geq 0 ]

    对于下面的式子:

    [ac+bd-ad-bc=(d-c)(b-a)geq 0 ]

    即可证明四个元素情况下(ad)一组,(bc)一组最优,将四个元素的情况进行拓展即可

    时间复杂度(O(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <typename _tp> inline void read(_tp&x){
    	char c11=getchar(),ob=0;x=0;
    	while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
    }
    
    int a[301003],n;
    
    int main(){
    	read(n);
    	for(int i=n;i;--i)read(a[i]);
    	sort(a+1,a+n+1);
    	ll ans = 0ll;
    	for(int i=n>>1;i;--i)
    		ans += (a[i]+a[n-i+1]) * (a[i]+a[n-i+1]);
    	cout << ans << endl;
    	return 0;
    }
    

    D. Lunar New Year and a Wander

    题意

    给定(n)(m)边无向图,从任意点出发,每次可以到达一个已达点(不一定是当前点)的相邻点,得到一个到达所有点的顺序(一个排列),问该顺序的最小字典序((n,mleq 10^5)

    思路

    和NOIp2018Day2T1有点像(不是同一道题啦( ̄▽ ̄))

    这题只要将相邻点加入堆,每次从堆里头选即可(要判是否已达)

    时间复杂度(O(nlog n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <typename _tp> inline void read(_tp&x){
    	char c11=getchar(),ob=0;x=0;
    	while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
    }
    
    const int N=101003;
    struct Edge{int v,nxt;}a[N+N];
    int head[N],vis[N],ans[N];
    int n,m,_;
    
    priority_queue <int> h;
    
    inline void ad(){
    	static int x,y;read(x),read(y);
    	a[++_].v=y,a[_].nxt=head[x],head[x]=_;
    	a[++_].v=x,a[_].nxt=head[y],head[y]=_;
    }
    
    int main(){
    	read(n),read(m);
    	while(m--)ad();
    	h.push(-1);vis[1]=1;
    	while(!h.empty()){
    		int x=-h.top();h.pop();
    		ans[++ans[0]]=x;
    		for(int&i=head[x];i;i=a[i].nxt)
    			if(!vis[a[i].v])h.push(-a[i].v),vis[a[i].v]=1;
    	}
    	for(int i=1;i<=n;++i)printf("%d ",ans[i]);
    	return 0;
    }
    

    E. Lunar New Year and Red Envelopes

    题意

    在一段长为(n)的时间内,Bob有(k)个红包可领取,第(i)个红包只能在时间([l_i,r_i])间领取,价值(w_i),选择后在时刻(d_i)前都不能再选其他红包

    Bob有自己的选红包策略,若某时刻Bob可以选取红包:

    • 选择当前可选红包中最大的
    • 若有多个红包最大,则选择(d)最大的

    所以Bob的收益其实是固定的。但Alice会来捣乱,她有(m)次机会扰乱Bob,每次可以使得Bob在一个时间点不选红包,问Bob的最小收益(Bob的策略恒定不变)

    (nleq 10^5,mleq 200,kleq 10^5)

    思路

    看到(m)这么小首先考虑Dp

    (f[i][j])表示前 (i) 的时间内使用了 (j) 次扰动的最小收益,首先尝试用(f[i][j])去更新(f[i+1][j+1])

    然后考虑若 (i) 时刻可以选红包,则选择的红包是固定的,若红包对应的(d)(D),收益(W),则可以用(f[i][j]+W)去尝试更新(f[D+1][j])

    (i) 时刻不能选红包,则可以用(f[i][j])去尝试更新(f[i+1][j])

    时刻 (i) 选择的红包可以用堆或线段树预处理

    时间复杂度(O(nm+klog k))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    template <typename _tp> inline void read(_tp&x){
    	char c11=getchar(),ob=0;x=0;
    	while(c11!='-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    	while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;
    }
    
    inline void cmin(ll&A,ll B){if(A>B)A=B;}
    
    const ll inf = 0x3f3f3f3f3f3f3f3f;
    const int N=101003;
    int d[N],w[N];
    ll f[N][203];
    int n,m,k,tp;
    
    struct node{
    	int l,r,d,w;
    	inline void in(){read(l),read(r),read(d),read(w);}
    	friend inline bool operator < (const node&A,const node&B) {return A.w!=B.w?A.w<B.w:A.d<B.d;}
    	friend inline bool operator > (const node&A,const node&B) {return A.w!=B.w?A.w>B.w:A.d>B.d;}
    }p[N],h[N];
    
    inline bool cmp (const node&A,const node&B) {return A.l<B.l;}
    
    inline void push(node x){
    	h[++tp]=x;
    	int pp=tp;
    	while(pp>1 and h[pp]>h[pp>>1])
    		swap(h[pp],h[pp>>1]),pp>>=1;
    }
    
    inline void pop(){
    	h[1]=h[tp--];int pp=1;
    	while(((pp<<1)<=tp and h[pp]<h[pp<<1]) or ((pp<<1|1)<=tp and h[pp]<h[pp<<1|1])){
    		pp<<=1;
    		if(h[pp]<h[pp|1])pp|=1;
    		swap(h[pp],h[pp>>1]);
    	}
    }
    
    int main(){
    	read(n),read(m),read(k);
    	for(int i=1;i<=k;++i)p[i].in();
    	sort(p+1,p+k+1,cmp);
    	for(int i=1,id=1;i<=n;++i){
    		while(id<=k and p[id].l<=i)push(p[id]),++id;
    		while(tp and h[1].r<i)pop();
    		if(tp)d[i]=h[1].d,w[i]=h[1].w;
    		else d[i]=i;
    	}
    	for(int i=n+1;~i;--i)
    	for(int j=m;~j;--j)
    		f[i][j] = inf;
       f[0][0]=0;
    	for(int i=0;i<=n;++i)
    	for(int j=0;j<=m;++j){
    		cmin(f[i+1][j+1],f[i][j]);
    		cmin(f[d[i]+1][j],f[i][j]+w[i]);
    	}
    	ll ans = inf;
    	for(int j=0;j<=m;++j)
    		cmin(ans,f[n+1][j]);
    	printf("%I64d
    ",ans);
    	return 0;
    }
    

    F. Lunar New Year and a Recursive Sequence

    题意

    已知一个序列的前(k-1)项为(1),第(n)项为(m),和其递推式:(f_i=prod_{j=1}^kf_{i-j}^{b_i})

    求该序列第 (k)

    (kleq 100,k<nleq 10^9),题目在模(998244353)条件下进行)

    思路

    比较无趣的一题

    首先递推式使用幂次积而非乘积和则考虑对指数进行递推,非常幸运地发现(f_i=1=f_k^0(i<k)),所以可以矩乘快速幂求解(f_n)(f_k)的多少次方

    现在的瓶颈在于求(x^tequiv b(mod p)),也就是求……k次剩余?

    想法将幂次再次化简,想到模数有原根(g=3),设
    (egin{cases} bequiv g^{b_0}pmod p\ xequiv g^{x_0}pmod p end{cases})

    将原式化简为(g^{x_0t}equiv g^{b_0}pmod p)

    (x_0cdot tequiv b_0pmod {(p-1)})

    裸bsgs可得(g_0),利用该式可裸exgcd求解(x_0)

    最后答案为(f_k=xequiv g^{x_0}pmod p)

    记得所有对幂次进行取模都要对(p-1)取模!!!

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    
    const int N=103,p=998244353;
    
    struct mat{
    	int n,m,a[N][N];
    	inline mat(){memset(a,0,sizeof a);}
    	inline mat(const int&nn,const int&mm):n(nn),m(mm){for(int i=0;i<n;++i)for(int j=0;j<m;++j)a[i][j]=0;}
    	friend inline mat operator * (const mat&A,const mat&B) {
    		mat res(A.n,B.m);
    		for(int i=0;i<A.n;++i)
    		for(int k=0;k<A.m;++k)if(A.a[i][k])
    		for(int j=0;j<B.m;++j)
    			res.a[i][j]=(res.a[i][j]+(ll)A.a[i][k]*B.a[k][j])%(p-1);
    		return res;
    	}
    };
    
    int exgcd(int a,int b,int&x,int&y){
    	if(!b){x=1,y=0;return a;}
    	int t=exgcd(b,a%b,y,x);
    	y-=a/b*x;return t;
    }
    
    inline int qpow(int A,int B){
    	int res(1);while(B){
    		if(B&1)res=(ll)res*A%p;
    		A=(ll)A*A%p,B>>=1;
    	}return res;
    }
    
    map <int,int> mp;
    int bsgs(int x){
    	int sr=40000;
    	int bs=1;
    	for(int i=0;i<=sr+10;++i)
    		mp[bs]=i,bs=3ll*bs%p;
    	int inv=qpow(qpow(3,sr),p-2);
    	for(int i=0;;++i){
    		if(mp.find(x)!=mp.end())
    			return i*sr+mp[x];
    		x=(ll)x*inv%p;
    	}
    }
    
    int main(){
    	int k,n,m;
    	scanf("%d",&k);
    	mat A(k,k);
    	for(int i=0;i<k-1;++i)
    		A.a[i][i+1]=1;
    	for(int i=k-1;~i;--i)
    		scanf("%d",&A.a[k-1][i]);
    	scanf("%d%d",&n,&m);
    	
    	mat res(k,1);
    	res.a[k-1][0]=1;
    	int B=n-k;
    	while(B){
    		if(B&1)res=A*res;
    		A=A*A,B>>=1;
    	}
    
    	int t=res.a[k-1][0],x,y;
    	int gcd=exgcd(t,p-1,x,y),gm=bsgs(m);
    	if(gm%gcd){puts("-1");return 0;}
    	x=(x+p-1)%(p-1);
    	x=(ll)x*(gm/gcd)%(p-1);
    	printf("%d
    ",qpow(3,x));
    	return 0;
    }
    
  • 相关阅读:
    /etc/vim/vimrc的一个的配置
    vim上下左右键输出A B
    数据结构-栈的实现之行编译器核心实现
    数据结构-栈的实现之括号匹配检测
    数据结构-栈的实现之数制转换
    数据结构-线性表的链式结构
    数据结构-栈的顺序结构两种方式
    简介
    数据结构-线性表的顺序结构
    NHibernate系列文章十六:使用程序集管理NHibernate项目(附程序下载)
  • 原文地址:https://www.cnblogs.com/penth/p/10352096.html
Copyright © 2020-2023  润新知