比赛记录:
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(sum∗logsum),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,j表示到第i个点,boss是半血还是死了
转移比较套路:
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)
∑Pj−j=(∑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]与r取max
那怎么办呢?
Solution 1
这就是吉司机线段树的功能了,不懂的可以网上搜吉司机线段树/segment tree beats!
时间复杂度 O ( n ∗ l o g n ) O(n*log_n) O(n∗logn)
Solution 2
但是这题还有一个性质没有用,由于
P
j
P_j
Pj是单调的,所以每次修改的区间一定是
(
l
,
r
′
]
,
(
r
′
≤
y
i
)
(l,r'],(r'\leq y_i)
(l,r′],(r′≤yi)所以只需要在普通线段树上二分到这个位置
r
′
r'
r′就可以了,然后再普通线段树上赋值操作就行了。
时间复杂度依然是
O
(
n
∗
l
o
g
n
)
O(n*log_n)
O(n∗logn)
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(n∗logn)
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
*/