Codeforces Round #775 (Div. 2, based on Moscow Open Olympiad in Informatics)

A. Game

题意:
你只能跳一次,可以从1的地方跳到任意1的地方,消耗的值为距离差,问最小消耗多少。
题解:
从前面最后的1跳到后面最前的1.

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 400005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
int a[111];
void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	int index1,index2;
	for(int i=1;i<=n;i++){
		if(a[i]==0)break;
		index1=i;
	}
	for(int i=n;i>=1;i--){
		if(a[i]==0)break;
		index2=i;
	}
	cout<<max(0,index2-index1)<<"\n";
}
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
    return 0;
}

B. Game of Ball Passing

题意:
有n个人踢球,每人个传球次数为 a i a_i ai,问最少需要几个球。
题解:
先考虑最大值很大的情况下,最大的人和吗,每个人都传一次球,还剩多少次传球就还需要多少球,若没剩下只需要1个球,若没人踢球答案为0.

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 400005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
int a[maxn];
void solve(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	ll sum=0;
	sort(a+1,a+1+n);
	for(int i=1;i<=n;i++){
		sum+=a[i];
	}
	sum-=a[n];
	if(a[n]==0)cout<<"0\n";
	else if(sum<a[n])cout<<a[n]-sum<<"\n";
	else cout<<1<<"\n";
}
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		solve();
	}
    return 0;
}

C. Weird Sum

题意:
问所有相同的值的哈夫曼距离总和。
题解:
把x,和y分别计算。因为每两两之间都会计算一次所以分开考虑不影响结果。分别对x,y排序后计算每一段会被计算几次就可以O(n)计算完。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 400005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
vector<int>h[111111],l[111111];
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int n,m,x;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>x;
			h[x].push_back(i);
			l[x].push_back(j);
		}
	}
	for(int i=1;i<=100000;i++)sort(h[i].begin(),h[i].end()),sort(l[i].begin(),l[i].end());
	ll ans=0;
	for(int i=1;i<=100000;i++){
		if(h[i].size()==0)continue;
		ll sumh=0,suml=0;
		for(int j=0;j<h[i].size();j++){
			sumh+=h[i][j];
			suml+=l[i][j];
		}
		for(int j=1;j<h[i].size();j++){
			sumh-=h[i][j-1];
			suml-=l[i][j-1];
			ans=(ll)ans+sumh-(ll)h[i][j-1]*(ll)(h[i].size()-j)+suml-(ll)l[i][j-1]*(ll)(h[i].size()-j);
		}
	}
	cout<<ans;
    return 0;
}

D. Integral Array

题意:
对于一个数组, 对 于 任 意 数 组 中 的 x , y , 有 y x ( 向 下 取 整 , y > = x ) 在 数 组 中 对于任意数组中的x,y,有\frac{y}{x}(向下取整,y>=x)在数组中 xyxy(,y>=x)。问每个数组是否符合这个条件。
题解:
若枚举除数,时间为 n n n\sqrt{n} nn ,会超时。观察到这题有一个上界,上界很小,可以离散化,对于每个数 y y y来说,若前面不存在 x x x,则后面 [ x ∗ y , x ∗ y + x ) [x*y,x*y+x) [xy,xy+x)必须没有值。这个可以通过前缀和O(1)判断。
对于每个 x x x只会计算 n x \frac{n}{x} xn次。
时间复杂度是一个调和级数O(nlogn)

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 400005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
int vis[1111111];
vector<int>a;
int sum[1111111];
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		int n,c;
		cin>>n>>c;
		a.clear();
		for(int i=1;i<=c;i++)vis[i]=0;
		for(int i=1,x;i<=n;i++)cin>>x,a.push_back(x),vis[x]=1;
		sort(a.begin(),a.end());
		if(a[0]!=1){
			cout<<"No\n";
			continue;
		}
		for(int i=1;i<=a[n-1];i++){
			sum[i]=sum[i-1]+vis[i];
		}
		a.erase(unique(a.begin(),a.end()),a.end());
		n=a.size();
		int f=1;
		for(int i=1;i<n;i++){
			for(int j=2;j*a[i]<=a[n-1];j++){
				int r=min(a[n-1],j*a[i]+a[i]-1),l=j*a[i];
				if(!vis[j]&&sum[r]-sum[l-1]>0){
					f=0;
					break;
				}
			}
		}
		if(f)cout<<"Yes\n";
		else cout<<"No\n";
	}
    return 0;
}

E. Tyler and Strings

题意:
你有一个S和T字符串,问S字符串排列组合有多少种方案是字典序小于T的。
题解:
枚举当前前 i − 1 i-1 i1位字符S和T相同,那么下一位小于T的数 n u m = ∑ i = 0 T [ i ] − 1 c n t [ i ] num=\sum_{i=0}^{T[i]-1}cnt[i] num=i=0T[i]1cnt[i],剩下的 n − i n-i ni位就可以随便排序。方案数位 n u m ∗ ( n − i ) ! c n t [ 0 ] ! c n t [ 1 ] ! . . . c n t [ N ] ! num*\frac{(n-i)!}{cnt[0]!cnt[1]!...cnt[N]!} numcnt[0]!cnt[1]!...cnt[N]!(ni)!,然后对于下一步来说,当前用掉一个T[i],cnt[t[i]]要减1.对于带修改的查询区间和,可以用线段树或树状数组维护。最后还需要判断一下,S是否是T的前缀,因为这样S全取也是一种方案,这种方案是因为数量少产生的,而我们计算的方案是字典序严格小的方案。最后答案+1.

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 200005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
ll a[maxn],b[maxn];
ll tree[maxn<<2],cnt[maxn];
ll s[maxn],t[maxn];
void init(){
	a[0]=1;
	for(int i=1;i<maxn;i++)a[i]=a[i-1]*i%MOD;
	b[maxn-1]=q_pow(a[maxn-1],MOD-2,MOD);
	for(int i=maxn-2;i>=0;i--)b[i]=b[i+1]*(i+1)%MOD;
}
void update(int id,int l,int r,int pos,ll x){
	if(l==r){
		tree[id]+=x;
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=pos)update(id<<1,l,mid,pos,x);
	else update(id<<1|1,mid+1,r,pos,x);
	tree[id]=tree[id<<1]+tree[id<<1|1];
}
ll query(int id,int l,int r,int lx,int ly){
	if(l>=lx&&r<=ly)return tree[id];
	int mid=(l+r)>>1;
	ll ans=0;
	if(mid>=lx)ans+=query(id<<1,l,mid,lx,ly);
	if(mid<ly)ans+=query(id<<1|1,mid+1,r,lx,ly);
	return ans;
}
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	init();
	ll n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++)cin>>s[i];
	for(int i=0;i<m;i++)cin>>t[i];
	for(int i=0;i<n;i++){
		cnt[s[i]]++;
		update(1,0,maxn-1,s[i],1);
	}
	ll inv=1;
	for(int i=0;i<maxn;i++)inv=inv*b[cnt[i]]%MOD;
	int f=0;
	ll ans=0;
	for(int i=0;i<min(n,m);i++){
		ll num=query(1,0,maxn-1,0,t[i]-1);
		ans=(ans+num*a[n-i-1]%MOD*inv%MOD)%MOD;
		if(cnt[t[i]]){
			inv=inv*a[cnt[t[i]]]%MOD;
			cnt[t[i]]--;
			inv=inv*b[cnt[t[i]]]%MOD;
			update(1,0,maxn-1,t[i],-1);
		}
		else{
			f=1;
			break;
		}
	}
	if(!f&&n<m)ans=(ans+1)%MOD;
	cout<<ans; 
    return 0;
}

F. Serious Business

题意:
一共有三层楼,只能往右走或往下走,到那 ( i , j ) (i,j) (i,j)位置可以获得 a i , j a_{i,j} ai,j的贡献,第二层不能走,除非第二层有优惠存在,那一段可以走,贡献要减去优惠。问走到 ( 3 , n ) (3,n) (3,n)的最大贡献。
题解:
分解成两块,一块为从 ( 1 , 1 ) − > ( 2 , i ) (1,1)->(2,i) (1,1)>(2,i)的贡献,以及从 ( 2 , i ) − > ( 3 , n ) (2,i)->(3,n) (2,i)>(3,n)的贡献。
p r e f [ k ] [ i ] pref[k][i] pref[k][i]代表第k层到i位置的贡献。
s [ i ] = p r e f [ 1 ] [ i ] − p r e f [ 2 ] [ i − 1 ] s[i]=pref[1][i]−pref[2][i-1] s[i]=pref[1][i]pref[2][i1]代表从i位置下第二层
f [ i ] = p r e f [ 2 ] [ i ] − p r e f [ 3 ] [ i − 1 ] + p r e f [ 3 ] [ n ] f[i]=pref[2][i]−pref[3][i-1]+pref[3][n] f[i]=pref[2][i]pref[3][i1]+pref[3][n]从i位置下第三层
对于一段 [ l , r ] , 优 惠 为 w [l,r],优惠为w [l,r]w的段,可以选择从i下第二层,从j下第三层, l < = i < = j < = r l<=i<=j<=r l<=i<=j<=r。用线段树维护。枚举每一个右端点。对于这一段来说,可以用下第一层的最大代价-w来更新r+1这个位置的值。这样子前面的值没有可能被更新,就可以算出答案。

#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <map>
#include <string>
#include <stack>
#include <cctype>
#include <vector>
#include <queue>
#include <set>
#include <utility>
#include <cassert>
#include <iomanip>
#include <deque>
#include <time.h>
#include <bitset>
using namespace std;
#define ll long long
#define maxn 500005
#define mod 1000000007
#define MOD 998244353
#define Mod 1000000009
#define eps 1e-10
const ll inf=0x3f3f3f3f3f3f3f3f;
const ll INF=0x3f3f3f3f;
const ll mod1=1e9+7;
const ll mod2=1e9+9;
template <typename T>
inline void read(T& X) {X = 0; int w = 0; char ch = 0;while (!isdigit(ch)) { w |= ch == '-'; ch = getchar(); }while (isdigit(ch)) X = (X << 3) + (X << 1) + (ch ^ 48), ch = getchar();if (w) X = -X;}
char F[200];inline void write(int x){if(x == 0){putchar('0');return;}int tmp = x > 0 ? x : -x;int cnt = 0;if(x < 0)putchar( '-' );while(tmp > 0){F[cnt++] = tmp % 10 + '0';tmp /= 10;}while(cnt > 0)putchar(F[--cnt]) ;}
template<typename T> void print(T x){if(x>9) print(x/10);putchar(x%10+'0');}
ll q_pow(ll x,ll y,ll M){ll ans=1;while(y){if(y%2){y--;ans=ans*x%M;}else {y/=2;x=x*x%M;}}return ans;}
struct node{
	ll x,y,z;
}tree[maxn<<2];
ll s[maxn],t[maxn],sum[4][maxn];
ll a[4][maxn];
vector<pair<ll,ll> >g[maxn];
void pushup(int id){
	tree[id].x=max(tree[id<<1].x,tree[id<<1|1].x);
	tree[id].y=max(tree[id<<1].y,tree[id<<1|1].y);
	tree[id].z=max({tree[id<<1].z,tree[id<<1|1].z,tree[id<<1].x+tree[id<<1|1].y});
	return ;
}
void build(int id,int l,int r){
	if(l==r){
		tree[id].x=s[l];
		tree[id].y=t[l];
		tree[id].z=s[l]+t[l];
		return;
	}
	int mid=(l+r)>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	pushup(id);
}
void update(int id,int l,int r,int pos,ll val){
	if(l==r){
		tree[id].x=max(tree[id].x,val);
		tree[id].z=max(tree[id].z,tree[id].x+tree[id].y);
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=pos)update(id<<1,l,mid,pos,val);
	else update(id<<1|1,mid+1,r,pos,val);
	pushup(id);
}
node query(int id,int l,int r,int lx,int ly){
	if(l>=lx&&r<=ly)return tree[id];
	int mid=(l+r)>>1;
	node ans,ans1,ans2;
	int f1=0,f2=0;
	if(mid>=lx)ans1=query(id<<1,l,mid,lx,ly),f1=1;
	if(mid<ly)ans2=query(id<<1|1,mid+1,r,lx,ly),f2=1;
	if(f1&&f2){
		ans.x=max(ans1.x,ans2.x);
		ans.y=max(ans1.y,ans2.y);
		ans.z=max({ans1.z,ans2.z,ans1.x+ans2.y});
		return ans;
	}
	if(f1)return ans1;
	if(f2)return ans2;
}
int main() 
{
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	ll n,q;
	cin>>n>>q;
	for(int i=1;i<=3;i++){
		for(int j=1;j<=n;j++){
			cin>>a[i][j];
			sum[i][j]=sum[i][j-1]+a[i][j];
		}
	}
	for(int i=1;i<=n;i++)s[i]=sum[1][i]-sum[2][i-1],t[i]=sum[2][i]-sum[3][i-1]+sum[3][n];
	for(ll i=1,l,r,w;i<=q;i++){
		cin>>l>>r>>w;
		g[r].push_back(make_pair(l,w));
	}
	build(1,1,n);
	ll ans=-inf;
	for(int i=1;i<=n;i++){
		for(auto &it:g[i]){
			ll l=it.first,w=it.second;
			node res=query(1,1,n,l,i);
			ans=max(ans,res.z-w);
			if(i<n)update(1,1,n,i+1,res.x-w);
		}
	}
	cout<<ans;
    return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值