Codeforces 1106 简要题解

传送门

A题

传送门
读错题还能过样例我给自己点个赞。
题意简述:给一个 01 01 01网格 S S S,问满足 S i , j = S i + 1 , j + 1 = S i + 1 , j − 1 = S i − 1 , j − 1 = S i − 1 , j + 1 S_{i,j}=S_{i+1,j+1}=S_{i+1,j-1}=S_{i-1,j-1}=S_{i-1,j+1} Si,j=Si+1,j+1=Si+1,j1=Si1,j1=Si1,j+1 X X X型个数,边长 ≤ 500 ‘ 在 这 里 插 入 代 码 片 ‘ \le500`在这里插入代码片` 500


直接模拟好吧别问博主是怎么看错题的
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=505;
int n;
char s[N][N];
int main(){
	scanf("%d",&n);
	for(ri i=1;i<=n;++i)scanf("%s",s[i]+1);
	int ans=0;
	for(ri i=2;i<n;++i)for(ri j=2;j<n;++j){
		if(s[i][j]=='X'&&s[i+1][j+1]=='X'&&s[i-1][j+1]=='X'&&s[i-1][j-1]=='X'&&s[i+1][j-1]=='X')++ans;
	}
	cout<<ans;
	return 0;
}

B题

传送门
题意简述:现在有 n n n个物品,每个物品有剩余体积和单位体积所需价格。
现在有 m m m个人依次来买物品,先买他指定的,如果不够就从最便宜的物品开始补,补到满足这个人需求量或者所有物品都没有为止,问每个人花费。


直接模拟,难点在于英语阅读,考虑用堆来维护每个物品的信息,这样摊下来时间复杂度是很正常的 O ( l o g n ) O(logn) O(logn),毕竟每个物品最多删掉一次。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=1e5+5;
int n,m,mp[N];
struct Node{int a,b,id;}p[N];
inline bool cmp(const Node&a,const Node&b){return a.b<b.b||(a.b==b.b&&a.id<b.id);}
priority_queue<Node>q;
inline bool operator<(const Node&a,const Node&b){return a.b>b.b||(a.b==b.b&&a.id>b.id);}
int main(){
	n=read(),m=read();
	for(ri i=1;i<=n;++i)p[i].a=read();
	for(ri i=1;i<=n;++i)p[i].b=read(),p[i].id=i;
	sort(p+1,p+n+1,cmp);
	for(ri i=1;i<=n;++i)mp[p[i].id]=i,p[i].id=i,q.push(p[i]);
	while(m--){
		int t=read(),d=read();
		if(p[mp[t]].a>=d){
			p[mp[t]].a-=d;
			cout<<(ll)d*p[mp[t]].b<<'\n';
			continue;
		}
		ll res=(ll)p[mp[t]].a*p[mp[t]].b;
		d-=p[mp[t]].a,p[mp[t]].a=0;
		while(d&&!q.empty()){
			Node tmp=q.top();
			if(!p[tmp.id].a)q.pop();
			else{
				if(p[tmp.id].a>=d){
					p[tmp.id].a-=d;
					res+=(ll)p[tmp.id].b*d;
					d=0;
				}
				else{
					res+=(ll)p[tmp.id].a*p[tmp.id].b;
					d-=p[tmp.id].a,p[tmp.id].a=0;
					q.pop();
				}
			}
		}
		if(d)cout<<0<<'\n';
		else cout<<res<<'\n';
	}
	return 0;
}

C题

传送门
题意简述:给 n n n个数让分成若干组(每组至少两个数),一组的代价是组内左右数之和的平方,问总代价最小值。


思路:
显然的一道贪心,把数列排序之后一头一尾配对即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=3e5+5;
int n,a[N];
ll ans=0;
int main(){
	n=read();
	for(ri i=1;i<=n;++i)a[i]=read();
	sort(a+1,a+n+1);
	for(ri i=1;i+i<=n;++i)ans+=(ll)(a[i]+a[n-i+1])*(a[i]+a[n-i+1]);
	cout<<ans;
	return 0;
}

D题

传送门
题意简述:给一张无向图,一个人从1号点出发,每次经过一个点时,如果是第一次经过某个点就把这个点加入一个序列中,问最后得到的序列的最小字典序。


思路:我们用一个堆来维护所有点的进出顺序即可,每次弹出一个点时把它能到的所有没入堆的点都压进堆里面,然后每次弹出编号最小的点即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const int N=1e5+5;
int n,m;
vector<int>e[N];
bool vis[N];
void bfs(){
	priority_queue<int,vector<int>,greater<int> >q;
	q.push(1);
	vis[1]=1;
	while(!q.empty()){
		int x=q.top();
		q.pop();
		cout<<x<<' ';
		for(ri i=0,v;i<e[x].size();++i){
			if(vis[v=e[x][i]])continue;
			vis[v]=1,q.push(v);
		}
	}
}
int main(){
	n=read(),m=read();
	for(ri i=1,u,v;i<=m;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	bfs();
	return 0;
}

E题

传送门
题意简述:现在有 k k k个钱包, n n n个时间点,每个钱包有四个属性 s , t , d , w s,t,d,w s,t,d,w表示这个钱包只能在时间点 [ s , t ] [s,t] [s,t]捡,价值为 w w w,并且如果在某个时间点捡了这个红包在时间点 d d d之后才能够捡下一个红包,现在可以强制 m m m个时间点不能捡红包,一个人会贪心从第一个时间点开始捡红包(即对于当前时间点如果能捡红包以价格为第一关键字 d d d为第二关键字捡红包)问他能获得的最小收益。


思路:设 f i , j f_{i,j} fi,j表示在前 i i i个时间点加 j j j次限制获得的最小收益。
然后我们用线段树预处理出每个时间点如果可以选择应该选择捡哪一个红包,然后直接转移即可。
代码:

#include<bits/stdc++.h>
#define ri register int
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();	
	return ans;
}
typedef long long ll;
const int N=1e5+5,M=205;
int n,m,k;
ll f[N][M];
struct Node{int s,t,d,w;}a[N];
typedef pair<int,int> pii;
pii pre[N];
namespace SGT{
	#define lc (p<<1)
	#define rc (p<<1|1)
	#define mid (l+r>>1)
	pii val[N<<2]=pii(0,0);
	inline void update(int p,int l,int r,int ql,int qr,pii v){
		if(ql<=l&&r<=qr){val[p]=max(val[p],v);return;}
		if(qr<=mid)update(lc,l,mid,ql,qr,v);
		else if(ql>mid)update(rc,mid+1,r,ql,qr,v);
		else update(lc,l,mid,ql,mid,v),update(rc,mid+1,r,mid+1,qr,v);
	}
	inline void query(int p,int l,int r,pii v){
		v=max(v,val[p]);
		if(l==r){pre[l]=v;return;}
		query(lc,l,mid,v),query(rc,mid+1,r,v);
	}
}
inline void update(ll&a,const ll&b){a=a<b?a:b;}
int main(){
	n=read(),m=read(),k=read();
	for(ri i=1;i<=k;++i){
		a[i].s=read(),a[i].t=read(),a[i].d=read(),a[i].w=read();
		SGT::update(1,1,n,a[i].s,a[i].t,pii(a[i].w,a[i].d));
	}
	for(ri i=0;i<=n+1;++i)for(ri j=0;j<=m+1;++j)f[i][j]=1e18;
	SGT::query(1,1,n,pii(0,0)),f[0][0]=0;
	for(ri i=1;i<=n;++i)cerr<<pre[i].first<<' '<<pre[i].second<<'\n'; 
	for(ri j=0;j<=m;++j){
		for(ri i=0;i<=n;++i){
			update(f[i+1][j+1],f[i][j]);
			if(pre[i]==pii(0,0))update(f[i+1][j],f[i][j]);
			else update(f[pre[i].second+1][j],f[i][j]+pre[i].first);
		}
	}
	ll ans=1e18;
	for(ri i=0;i<=m;++i)update(ans,f[n+1][i]);
	return cout<<ans,0;
}

F题

传送门
题意简述:原本有一个 f f f数列,满足 ∀ i &gt; k , f i = ( ∏ j = 1 k f i − j b j ) m o d &ThinSpace;&ThinSpace; P , P = 998244353 \forall i&gt;k,f_i=(\prod_{j=1}^kf_{i-j}^{b_j})\mod P,P=998244353 i>k,fi=(j=1kfijbj)modP,P=998244353,现在已知 f 1 f_1 f1~ f k − 1 = 1 f_{k-1}=1 fk1=1,以及 f n ( n ≥ k ) = m f_n(n\ge k)=m fn(nk)=m,求 f k f_k fk的可能值。


思路:
先考虑没有模数的做法:
由于 f 1 f_1 f1~ f k − 1 f_{k-1} fk1都是 1 1 1,因此 f k + 1 f_{k+1} fk+1 f k f_k fk的幂, f k + 2 f_{k+2} fk+2 f k f_k fk的幂,…, f n = m f_n=m fn=m f k f_k fk的幂,这启发我们把 f 1 f_1 f1 ~ f k − 1 f_{k-1} fk1都看成 ( f k ) 0 (f_k)^0 (fk)0,于是我们可以矩阵快速幂递推出 m = f n = ( f k ) p m=f_n=(f_k)^p m=fn=(fk)p
然后考虑同时取离散对数。
这样令 g g g为原根, f k = g k , m = g q = &gt; ( g k ) p = g q = &gt; p k ≡ q m o d &ThinSpace;&ThinSpace; ( P − 1 ) f_k=g^k,m=g^q=&gt;(g^k)^p=g^q=&gt;pk\equiv q\mod (P-1) fk=gk,m=gq=>(gk)p=gq=>pkqmod(P1)
然后就只用分别把 p , q p,q p,q解出来然后跑一遍 e x g c d exgcd exgcd即可。
代码:

#include<bits/stdc++.h>
#include<tr1/unordered_map>
#define ri register int
using namespace std;
const int mod=998244353,mod1=998244352,N=105,Mod=1e6+7;
int k,b[N],n,m;
typedef long long ll;
inline int add(int a,int b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a>=b?a-b:a-b+mod;}
inline int mul(int a,int b){return (ll)a*b%mod;}
inline int add1(int a,int b){return a+b>=mod1?a+b-mod1:a+b;}
inline int dec1(int a,int b){return a>=b?a-b:a-b+mod1;}
inline int mul1(int a,int b){return (ll)a*b%mod1;}
struct Matrix{
	int a[N][N];
	Matrix(int val=0){memset(a,0,sizeof(a));for(ri i=0;i<k;++i)a[i][i]=val;}
	friend inline Matrix operator*(const Matrix&a,const Matrix&b){
		Matrix ret(0);
		for(ri i=0;i<k;++i)for(ri l=0;l<k;++l)if(a.a[i][l])
			for(ri j=0;j<k;++j)ret.a[i][j]=add1(ret.a[i][j],mul1(a.a[i][l],b.a[l][j]));
		return ret;
	}
	friend inline Matrix operator^(Matrix a,int p){
		Matrix ret(1);
		for(;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
		return ret;
	}
}a;
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)ret=mul(ret,a);return ret;}
inline int query(){
	int sqr=sqrt(mod);
	int tmp=ksm(3,sqr);
	tr1::unordered_map<int,int>S;
	S.clear();
	for(ri i=0,mult=m,inv=ksm(3,mod-2);i<sqr;++i,mult=mul(mult,inv))S[mult]=i+1;
	for(ri i=0,mult=1,stp=ksm(3,sqr);i*sqr<=mod;++i,mult=mul(mult,stp))if(S[mult])return (S[mult]-1)+i*sqr;
	return -1;
}
inline void exgcd(int a,int b,int&x,int&y){
	if(!b){x=1,y=0;return;}
	exgcd(b,a-a/b*b,x,y);
	int t=x;
	x=y,y=t-a/b*y;
}
int main(){
	scanf("%d",&k);
	int sum=0;
	for(ri i=1;i<=k;++i)scanf("%d",&b[i]),sum=add1(sum,b[i]);
	scanf("%d%d",&n,&m);
	for(ri i=1;i<k;++i)a.a[i][i-1]=1;
	for(ri i=0;i<k;++i)a.a[i][k-1]=b[k-i];
	for(ri i=0;i<k;++i,puts(""))for(ri j=0;j<k;++j)cerr<<a.a[i][j]<<' ';
	a=a^(n-k);
	for(ri i=0;i<k;++i,puts(""))for(ri j=0;j<k;++j)cerr<<a.a[i][j]<<' ';
	int p=a.a[k-1][k-1];
	int q=query();
	if(q==-1)return puts("-1"),0;
	int x,y,a=p,b=mod1,c=q,g=__gcd(a,b);
	if(c%g)return puts("-1"),0;
	a/=g,b/=g,c/=g,exgcd(a,b,x,y);
	x=(x%mod1+mod1)%mod1,x=(ll)x*c%mod1;
	cout<<ksm(3,x);
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值