Codeforces Round #666 (Div. 1) ABCD(E)题解

89 篇文章 1 订阅

比赛记录:

00:00:00  Participant has been assigned to the room 21
00:11:28  A  Wrong answer on pretest 1 [pretests]
00:13:07  A  Skipped [main tests]
00:22:08  B  Accepted [main tests]
01:22:08  C  Accepted [main tests]
01:37:57  A  Accepted [main tests]
01:39:10  A  has been locked
01:41:24  B  has been locked

在这里插入图片描述

A

一个非常明显的做法就是第一次选长度为n的,第二次选长度为n-1的,前面的n-1个通过n和n-1的差为1来解决。最后一个直接选长度为1的来弄,但是要特判1

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int a[100000+1];
int main(){
	int n;
	R(n);
	rb(i,1,n)
		R(a[i]);
	if(n==1){
		cout<<"1 1"<<endl;
		cout<<1<<endl;
		cout<<"1 1"<<endl;
		cout<<1<<endl;
		a[1]++;
		cout<<"1 1\n";
		a[1]++;
		cout<<-(a[1])<<endl;
		return 0;
	}
	cout<<1<<" "<<n<<endl;
	rb(i,1,n-1){
		int need=0-a[i];
		cout<<1ll*need*n<<" ";
	}
	if(a[n]+n!=0){
		cout<<n<<endl;
		a[n]+=n;
	}
	else{
		cout<<2*n<<endl;
		a[n]+=2*n;
	}
	cout<<1<<" "<<n-1<<endl;
	rb(i,1,n-1){
		int need=0-a[i];
		cout<<-1ll*need*(n-1)<<" "; 
	}
	cout<<endl;
	cout<<n<<" "<<n<<endl;
	cout<<-(a[n])<<endl;
	return 0;
}

B

贪心,优先选择最大的。

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
int a[101];
int main(){
	int t;
	cin>>t;
	while(t--){
		int n;
		R(n);
		rb(i,1,n)
			R(a[i]);
		priority_queue<int> q;
		rb(i,1,n){
			q.push(a[i]);
		}
		int is=0;
		mp las=II(-1,-1);
		while(1){
			if(las.FIR>0)
				q.push(las.FIR);
			swap(las.FIR,las.SEC);
			if(q.empty()){
				break;
			}
			las.SEC=q.top();
			q.pop();
			las.SEC--;
			is^=1;
		}
		cout<<(is? "T":"HL")<<endl;
	}
	return 0;
}

这样时间复杂度为 O ( s u m ∗ l o g s u m ) , s u m = ∑ i = 1 n a i O(sum*log_{sum}),sum=\sum_{i=1}^na_i O(sumlogsum),sum=i=1nai。已经可以过了。
但是这题也存在 O ( n l o g n ) O(nlogn) O(nlogn)的做法:
显然如果存在 a j > ∑ i ≠ j a i a_j>\sum_{i\neq j} a_i aj>i=jai的化先手必胜,因为他只需要一直拿第j堆就行了。
如果不是这样的化通过模拟上述的贪心一定是会把石子全部取完的。因为如果一个人取完之后连上他的只剩下一堆的化,则前面的那一个人取走的一定是一个一个石子的堆,不是最大的那一堆。
这是tourist的代码(4分钟切掉B的神仙)

/**
 *    author:  tourist
 *    created: 30.08.2020 17:42:50       
**/
#include <bits/stdc++.h>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0);
  int tt;
  cin >> tt;
  while (tt--) {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) {
      cin >> a[i];
    }
    int mx = *max_element(a.begin(), a.end());
    int sum = accumulate(a.begin(), a.end(), 0);
    if (mx > sum - mx || sum % 2 == 1) {
      cout << "T" << '\n';
    } else {
      cout << "HL" << '\n';
    }
  }
  return 0;
}

C

一个dp题,显然一个人的路径一定是先往前走,然后再一对点之间来回一次,再往前走再在一对点之间来回,最后走到终点又走回来。
状态: f i , j 表 示 到 第 i 个 点 , b o s s 是 半 血 还 是 死 了 f_{i,j}表示到第i个点,boss是半血还是死了 fi,jiboss
转移比较套路:
O ( n ) O(n) O(n)但不知道为什么跑得巨慢。。。
Code:

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
LL f[1000000+1][2];
int n,a[1000000+1];
LL r1,r2,r3,d;
LL half(int x){
	LL rest=r2;
	LL tmp=min(r1,r3)*x;
	tmp+=r1;
	return min(rest,tmp);
} 
LL two(int x){
	LL rest=min(r1,r3)*(x)+r3;
	return min(rest,half(x)+min(min(r1,r2),r3));
}
int main(){
	R(n);
	cin>>r1>>r2>>r3>>d;
	rb(i,1,n)
		R(a[i]);
	memset(f,127,sizeof(f));
	f[0][0]=0;
	rb(i,1,n){
		rep(j,2){
			if(j==0)
			{
				LL cost=f[i-1][0];
				if(f[i-1][0]<1e18){
					if(i!=1)
					cost+=d;
					cost+=1ll*a[i]*min(r1,r3);
					cost+=r3;
					f[i][j]=cost;
				}
				if(f[i-1][1]<1e18){
					cost=f[i-1][1];
					cost+=3ll*d;
					cost+=min(min(r1,r2),r3);
					cost+=two(a[i]);//分两次
					check_min(f[i][j],cost);
				} 
			}
			else{
				LL cost=f[i-1][0];
				if(f[i-1][0]<1e18){
					if(i!=1)
					cost+=d;
					cost+=half(a[i]);//一半 
					f[i][1]=cost;
				}
				if(f[i-1][1]<1e18){
					cost=f[i-1][1];
					cost+=3ll*d;
					cost+=min(min(r1,r2),r3);
					cost+=half(a[i]);
					check_min(f[i][j],cost);	
				}
			}
		}
	}
	LL rest=f[n][0];
//	cout<<rest<<endl;
	LL tmp=min(r1,r3)*a[n]+r3;
	rl(i,n-1,1){
		tmp+=d*2;
		tmp+=two(a[i]);
//		cout<<tmp<< " "<<f[i-1][0]<<" "<<(i==1? 0:d)<<endl;
		if(tmp>=rest) break;
		rest=min(rest,tmp+(i==1? 0:d)+f[i-1][0]);
	}
	cout<<rest<<endl;
	return 0;
}

D

首先将坐标离散化,假设离散化后的行数列数分别为 N , M N,M N,M
首先可以可以想到枚举子矩形的一维。我们可以枚举子矩形的左下角所在的行数,假设是 i i i,那么我们设 P j P_j Pj表示从 j j j向右走要走到多元才可以使得每一个颜色都有?
这样可以用two-pointers O ( n 2 ) O(n^2) O(n2)的预处理。
然后对于此类情况不合法的答案是 ∑ P j − j = ( ∑ P j ) − ( ∑ j = 1 M ) \sum P_j-j=(\sum P_j)-(\sum_{j=1}^M) Pjj=(Pj)(j=1M)后面的这个 ( ∑ j = 1 M ) (\sum_{j=1}^M) (j=1M)显然可以预处理,所以答案就变成了求 ∑ P j \sum P_j Pj
然后对于左下角的行数为 i i i,右上角的行数不为1的情况怎么办呢?
我们不妨从上到下考虑右上角的行数。假设由 j → ( j + 1 ) j\rightarrow (j+1) j(j+1),那么有一些 P j P_j Pj会变大。
那么这些是哪些呢?我们假设在 j → ( j + 1 ) j\rightarrow (j+1) j(j+1)后,颜色 c c c的y坐标所构成的集合为 h a v e c have_c havec。当前考虑去除点 ( x i , y i , c i ) (x_i,y_i,c_i) (xi,yi,ci),那么我们就在 h a v e c have_c havec中找到 y i y_i yi的前一个和后一个,分别为 l , r l,r l,r
那么区间 ( l , y i ] (l,y_i] (l,yi]可能会向由拓展到 r r r,所以就是让 ( l , y i ] 与 r 取 max ⁡ (l,y_i]与r取\max (l,yi]rmax
那怎么办呢?

Solution 1

这就是吉司机线段树的功能了,不懂的可以网上搜吉司机线段树/segment tree beats!

时间复杂度 O ( n ∗ l o g n ) O(n*log_n) O(nlogn)

Solution 2

但是这题还有一个性质没有用,由于 P j P_j Pj是单调的,所以每次修改的区间一定是 ( l , r ′ ] , ( r ′ ≤ y i ) (l,r'],(r'\leq y_i) (l,r],(ryi)所以只需要在普通线段树上二分到这个位置 r ′ r' r就可以了,然后再普通线段树上赋值操作就行了。
时间复杂度依然是 O ( n ∗ l o g n ) O(n*log_n) O(nlogn)

Solution 3

利用set将一些相同的位置压缩,记为 ( a , b ) (a,b) (a,b)有b个连续的a。
S o l u t i o n 2 Solution 2 Solution2类似,也可以二分出那个 r ′ r' r,然后直接在set中修改。
这样每次会多一个串。
时间复杂度: O ( n ∗ l o g n ) O(n*log_n) O(nlogn)

Solution 4

枚举左上角 ( x , y ) (x,y) (x,y)
也是找到所有不符合的情况数,用每一个颜色维护一个单调栈,也就是说如果所有的过这个点的子矩形都过点 j j j,则i就不加入。(具体的什么我还没想好)
程序将于明天上传。
一下代码是吉司机线段树,set的可以看tourist,单调栈的可以看ksun.

/*
{By GWj
*/
#pragma GCC optimize(2)
#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define fastio ios::sync_with_stdio(false)
#define R(a) cin>>a
#define R2(a,b) cin>>a>>b
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/
map<int,int> mx,my;
const int MOD=1e9+7;
int n,N,M,k,l,realx[2055],realy[2055],x[2005],y[2005],c[2005];
LL quick(int A,int B){
	if(!B) return 1ll;
	LL tmp=quick(A,B>>1);
	tmp*=tmp;
	tmp%=MOD;
	if(B&1){
		tmp*=A;
		tmp%=MOD;
	}
	return tmp;
}
multiset<int> s[2005];
multiset<int> :: IT ite; 
vector<mp> have[2005];
LL pre1[2005],pre2[2005],cnt[2005]; // \sum diff , \sum diff*real
struct node{
	LL mi,se,sum,rest;/*最小值 次小值 最小值的\sum diff[i]*/
};
const int MAXN=1<<11;
node tree[MAXN+MAXN];
int tag[MAXN+MAXN];
void build(int index){
	if(index>=MAXN){
		tree[index]=node{0,INF,realy[index-MAXN+1]-realy[index-MAXN],0};
	}
	else{
		build(index<<1);
		build(index<<1|1);
		tree[index].mi=0;
		tree[index].se=INF;
		tree[index].sum=tree[index<<1].sum+tree[index<<1|1].sum;
		tree[index].sum%=MOD;
		tree[index].rest=0;
	} 
}
void push_up(int index){
	tree[index].mi=min(tree[index<<1].mi,tree[index<<1|1].mi);
	tree[index].se=INF;
	if(tree[index<<1].mi!=tree[index].mi){
		check_min(tree[index].se,tree[index<<1].mi);
	}
	else{
		check_min(tree[index].se,tree[index<<1].se);
	}
	if(tree[index<<1|1].mi!=tree[index].mi){
		check_min(tree[index].se,tree[index<<1|1].mi);
	}
	else{
		check_min(tree[index].se,tree[index<<1|1].se);
	}
	tree[index].sum=0;
	if(tree[index<<1].mi==tree[index].mi){
		tree[index].sum+=tree[index<<1].sum;
		tree[index].sum%=MOD;
	}
	if(tree[index<<1|1].mi==tree[index].mi){
		tree[index].sum+=tree[index<<1|1].sum;
		tree[index].sum%=MOD;
	}
	tree[index].rest=tree[index<<1].rest+tree[index<<1|1].rest;
	tree[index].rest%=MOD;
} 
void add_tag(int index,int x){
	if(x<=tree[index].mi) return;
	if(x<tree[index].se){
		tag[index]=max(tag[index],x);
		tree[index].rest+=1ll*(x-tree[index].mi)*tree[index].sum;
		tree[index].rest%=MOD;
		tree[index].mi=x;
		return;
	}
	add_tag(index<<1,x);
	add_tag(index<<1|1,x);
	push_up(index);
}
void push_down(int index){
	add_tag(index<<1,tag[index]);
	add_tag(index<<1|1,tag[index]);
	tag[index]=0;
}

void Check_Max(int a,int b,int val,int now=1,int l=1,int r=MAXN+1){
	if(r<=a||l>=b){
		return;
	}	
	if(r<=b&&l>=a){
		add_tag(now,val);
		return;
	}
	push_down(now);
	int mid=(l+r)>>1;
	Check_Max(a,b,val,now<<1,l,mid);
	Check_Max(a,b,val,now<<1|1,mid,r);
	push_up(now);
}
signed main(){
	R2(n,k);
	R(l);
	int cnt_=0;
	mx[0]=my[0]=1;
	mx[l+1]=my[l+1]=1;
	rb(i,1,n){
		cin>>x[i]>>y[i]>>c[i];
		x[i]++;
		y[i]++;
		mx[x[i]]=1;
		my[y[i]]=1;
	}
	cnt_=0;
	for(map<int,int> :: IT ite=mx.begin();ite!=mx.end();ite++){realx[cnt_]=ite->FIR;ite->SEC=cnt_++;}
	N=cnt_-2;
	cnt_=0;
	for(map<int,int> :: IT ite=my.begin();ite!=my.end();ite++){realy[cnt_]=ite->FIR;ite->SEC=cnt_++;}
	M=cnt_-2;
	rb(i,1,n)
		x[i]=mx[x[i]],y[i]=my[y[i]];
	LL rest=0;
	rb(i,1,M){
		pre1[i]=pre1[i-1];
		pre2[i]=pre2[i-1];
		pre1[i]+=realy[i]-realy[i-1];
		pre1[i]%=MOD;
		pre2[i]+=1ll*(realy[i]-realy[i-1])*realy[i]%MOD;
		pre2[i]%=MOD;
	}	
	LL hooo=pre1[M];
	hooo*=(l+1);
	hooo%=MOD;
	rb(bot,1,N){
		memset(tag,0,sizeof(tag));
		build(1);
		int pi=1,have_=0;
		rb(i,1,k)
			cnt[i]=0;
		vector<mp> v;
		rb(i,1,N)
			have[i].clear();
		rb(i,1,k)
			s[i].clear();
		rb(i,1,n)
		{
			if(x[i]<=bot){
				v.PB(II(y[i],c[i]));
				s[c[i]].insert(y[i]);
				have[x[i]].PB(II(y[i],c[i]));
			}
		}
		rb(i,1,k)
			s[i].insert(M+1);
		sort(ALL(v));
		int is=0;		
		rb(i,1,M){
			while(is<v.size()&&v[is].FIR<=i){
				if(!cnt[v[is].SEC]){
					have_++;
				}
				cnt[v[is].SEC]++;
				is++;
			}
			if(have_==k)  break;
			pi=i+1;
		}
		int iss=0;
		rb(i,1,M){
			while(have_!=k&&is<v.size()){
				pi=v[is].FIR;
				if(!cnt[v[is].SEC]){
					have_++;
				}
				cnt[v[is].SEC]++;
				is++;
			}
			while(is<v.size()&&v[is].FIR<=pi){
				cnt[v[is].SEC]++;
				is++;
			}
			if(have_!=k){
				pi=M+1;
			}
			Check_Max(i,i+1,realy[pi]);	
			while(iss<v.size()&&v[iss].FIR<=i){
				cnt[v[iss].SEC]--;
				if(!cnt[v[iss].SEC]){
					have_--;
				}
				iss++;
			}
		}
		rb(top,1,bot){
			for(auto it:have[top-1]){
				s[it.SEC].erase(s[it.SEC].find(it.FIR));
					int l,r;
					ite=s[it.SEC].lower_bound(it.FIR);
					r=realy[*ite];
					ite=s[it.SEC].upper_bound(it.FIR);
					if(ite!=s[it.SEC].begin()){
						ite--;
						l=*ite;
					}
					else{
						l=0;
					}
					l++;
					if(l<=it.FIR)
					Check_Max(l,it.FIR+1,r);
			} 
			rest+=((hooo-tree[1].rest)+MOD)%MOD*(realx[top]-realx[top-1])%MOD*(realx[bot+1]-realx[bot])%MOD;//p[i]*diff[i]
			rest%=MOD;
		}
	}
	cout<<rest<<endl;
	return 0;
}
/*
20 7 100
37 42 3
39 80 3
37 53 2
39 73 6
35 10 1
88 10 5
9 20 4
0 42 2
0 97 4
47 97 7
68 38 5
89 80 3
82 10 6
37 28 5
35 3 6
37 80 4
35 53 5
9 42 3
88 42 7
39 10 2
*/

E(目前不太会,正在尝试)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值