比赛传送门
传送门
排个序然后取 a [ n − 1 ] − 1 a[n-1]-1 a[n−1]−1与 n − 2 n-2 n−2的最小值。
代码:
#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
#define gc getchar
int ans=0;
bool f=1;
char ch=gc();
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5;
int n,a[N];
int main(){
for(ri tt=read();tt;--tt){
n=read();
for(ri i=1;i<=n;++i)a[i]=read();
sort(a+1,a+n+1);
cout<<min(n-2,a[n-1]-1)<<'\n';
}
return 0;
}
传送门
随便写个双向链表然后按照题意模拟。
代码:
#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
#define gc getchar
int ans=0;
bool f=1;
char ch=gc();
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5;
int n,a[N],pos[N],pre[N],suf[N];
int main(){
n=read();
for(ri i=1;i<=n;++i)a[i]=read(),pos[a[i]]=i;
for(ri i=1;i<=n;++i)pre[a[i]]=a[i-1],suf[a[i]]=a[i+1];
pre[0]=a[1],suf[n+1]=a[n];
int t;
for(t=n;t;--t){
if(pre[t]!=t-1&&suf[t]!=t-1)break;
suf[pre[t]]=suf[t];
pre[suf[t]]=pre[t];
}
if(t>1)puts("No");
else puts("Yes");
return 0;
}
传送门
考虑转化问题。
变成求 a i − a i − 1 a_i-a_{i-1} ai−ai−1的前 k − 1 k-1 k−1大值,然后用 a n − a 1 a_n-a_1 an−a1减去它们的和即可。
代码:
#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
#define gc getchar
int ans=0;
bool f=1;
char ch=gc();
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=3e5+5;
int n,k,a[N];
int main(){
n=read(),k=read();
for(ri i=1;i<=n;++i)a[i]=read();
n=unique(a+1,a+n+1)-a-1;
if(n<=k)return puts("0"),0;
int ans=a[n]-a[1];
for(ri i=2;i<=n;++i)a[i-1]=a[i]-a[i-1];
sort(a+1,a+n);
for(ri i=n-1,j=1;j<k;--i,++j)ans-=a[i];
cout<<ans;
return 0;
}
传送门
这题应该可以 O ( n ) O(n) O(n),但博主太懒了于是写了个 O ( n m ) O(nm) O(nm)的,设 f i f_i fi表示区间左端点为 i i i时的最优值。
考虑从右向左枚举这个 i i i。
于是每次对于一个左端点 i i i只需要考虑一下两种情况:
两种情况取个最大值即可。
#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
#define gc getchar
int ans=0;
bool f=1;
char ch=gc();
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=998244353;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=3e5+5;
int n,m,k,a[N];
ll f[N],sum[N];
int main(){
n=read(),m=read(),k=read();
for(ri i=1;i<=n;++i)a[i]=read(),sum[i]=sum[i-1]+a[i];
ll ans=0;
for(ri i=n;i;--i){
f[i]=0;
for(ri j=i,up=min(i+m-1,n);j<=up;++j)f[i]=max(f[i],sum[j]-sum[i-1]-k);
if(i+m-1<n)f[i]=max(f[i],sum[i+m-1]-sum[i-1]-k+f[i+m]);
ans=max(ans,f[i]);
}
cout<<ans;
return 0;
}
传送门
离散化后有个边界写错了 w a wa wa了三次。。。
先把左右端点离散化了。
考虑从左往右扫
考虑对于一个区间 [ l i , r i ] [l_i,r_i] [li,ri],只有满足 r j ≤ l i r_j\le l_i rj≤li的区间才能对它产生影响,且根据题意,我们对每个区间要维护这么一些信息。
设对于点 p p p,有 k k k种方案在 p p p结束,且总空位数最小值等于 d i s dis dis,那么如果 p p p可以转移到 q q q,总空位数应该变成 d i s + q − p = d i s − p + q dis+q-p=dis-p+q dis+q−p=dis−p+q,由此我们发现如果从一个 r j r_j rj可以转移到一个 l i l_i li,一定满足 d i s r j − r j = min r k ≤ l i { d i s r k − r k } dis_{r_j}-r_j=\min_{r_k\le l_i}\{dis_{r_k}-r_k\} disrj−rj=minrk≤li{disrk−rk},于是我们在线段树上面维护 d i s p − p dis_{p}-p disp−p的最小值与这个最小值的出现次数即可。
代码:
#include
#define ri register int
#define fi first
#define se second
using namespace std;
inline int read(){
#define gc getchar
int ans=0;
bool f=1;
char ch=gc();
while(!isdigit(ch))f^=ch=='-',ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return f?ans:-ans;
}
typedef pair<int,int> pii;
typedef long long ll;
const int mod=1e9+7;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
const int N=2e5+5,M=4e5+5;
int n,mp[M],sig=0;
pii a[N];
struct Node{
int a,b;
friend inline Node operator+(const Node&a,const Node&b){
if(a.a<b.a)return a;
if(a.a>b.a)return b;
return (Node){a.a,add(a.b,b.b)};
}
};
namespace sgt{
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (l+r>>1)
Node T[M<<2];
inline void build(int p,int l,int r){
T[p]=(Node){0x3f3f3f3f,0};
if(l==r)return;
build(lc,l,mid),build(rc,mid+1,r);
}
inline void update(int p,int l,int r,int k,Node v){
if(l==r){T[p]=T[p]+v;return;}
k<=mid?update(lc,l,mid,k,v):update(rc,mid+1,r,k,v);
T[p]=T[lc]+T[rc];
}
inline Node query(int p,int l,int r,int ql,int qr){
if(ql<=l&&r<=qr)return T[p];
if(qr<=mid)return query(lc,l,mid,ql,qr);
if(ql>mid)return query(rc,mid+1,r,ql,qr);
return query(lc,l,mid,ql,qr)+query(rc,mid+1,r,ql,qr);
}
#undef lc
#undef rc
}
int main(){
n=read();
for(ri i=1;i<=n;++i){
a[i].se=read();
a[i].fi=read();
mp[++sig]=a[i].fi;
mp[++sig]=a[i].se;
}
sort(mp+1,mp+sig+1);
sort(a+1,a+n+1);
sig=unique(mp+1,mp+sig+1)-mp-1;
sgt::build(1,1,sig);
int mx=mp[lower_bound(mp+1,mp+sig+1,a[n].fi)-mp];
for(ri p1,p2,i=1;i<=n;++i){
if(a[i].se>mx)continue;
p1=lower_bound(mp+1,mp+sig+1,a[i].fi)-mp;
p2=lower_bound(mp+1,mp+sig+1,a[i].se)-mp;
Node t=sgt::query(1,1,sig,1,p1);
if(t.a==0x3f3f3f3f)sgt::update(1,1,sig,p2,(Node){a[i].fi-a[i].se,1});
else sgt::update(1,1,sig,p2,(Node){t.a+a[i].fi-a[i].se,t.b});
}
Node ans=(Node){0x3f3f3f3f,0};
for(ri p1,p2,i=1;i<=n;++i){
if(a[i].se<=mx)continue;
p1=lower_bound(mp+1,mp+sig+1,a[i].fi)-mp;
p2=lower_bound(mp+1,mp+sig+1,a[i].se)-mp;
Node t=sgt::query(1,1,sig,1,p1);
if(t.a==0x3f3f3f3f)ans=ans+(Node){t.a,1};
else ans=ans+(Node){t.a+a[i].fi,t.b};
}
cout<<ans.b;
return 0;
}
传送门
感觉是道我要想很久的题,然而比赛时由于看错题了没做出来,下来看题解突然发现自己看错题。。。
考虑只有一条布袋咋做。
发现由于最多只能走 3 3 3步。我们可以设计状态 f i , 0 / 1 , 0 / 1 , 0 / 1 f_{i,0/1,0/1,0/1} fi,0/1,0/1,0/1表示前 i i i个格子,从最后三个格子出发的 n p np np状态为 0 / 1 , 0 / 1 , 0 / 1 0/1,0/1,0/1 0/1,0/1,0/1的方案数,这样可以根据题目中的限制数组写出状态转移时。
但是由于布条的长度很大,直接做会 T L E TLE TLE,于是可以矩阵快速幂优化,发现每个给出了颜色的地方跟没有颜色的地方转移式不太一样,于是我们对于每一种颜色预处理出转移式,然后每两个涂色格子之间的空格子用矩阵快速幂转移即可,复杂度是 O ( m ∗ a 3 l o g 2 a n ) O(m*a^3log_2a_n) O(m∗a3log2an)的, a a a是矩阵边长。
能不能优化时间复杂度呢?
显然是可以的,由于最后我们只用求一个向量而不是整个矩阵的值,因此我们可以预处理出 2 k 2^k 2k个空格子的矩阵,然后每次用倍增转移,这样可以做到 O ( m ∗ a 2 ∗ l o g 2 a n ) O(m*a^2*log_2a_n) O(m∗a2∗log2an)的时间复杂度。
现在考虑有多个布条的情况。
发现只需要把 n p np np的状态变成 s g sg sg函数值然后最后用类似分组背包的方法转移即可,由于这个题里的 s g sg sg值是不超过 3 3 3的,因此总复杂度是 O ( m a 2 l o g 2 a n ) O(ma^2log_2a_n) O(ma2log2an),其中 a = 64 a=64 a=64。
代码:
#include
#define ri register int
#define fi first
#define se second
using namespace std;
const int rlen=1<<18|1;
inline char gc(){
static char buf[rlen],*ib,*ob;
(ib==ob)&&(ob=(ib=buf)+fread(buf,1,rlen,stdin));
return ib==ob?-1:*ib++;
}
inline int read(){
int ans=0;
char ch=gc();
while(!isdigit(ch))ch=gc();
while(isdigit(ch))ans=((ans<<2)+ans<<1)+(ch^48),ch=gc();
return ans;
}
const int mod=998244353;
typedef long long ll;
inline int add(const int&a,const int&b){return a+b>=mod?a+b-mod:a+b;}
inline int dec(const int&a,const int&b){return a>=b?a-b:a-b+mod;}
inline int mul(const int&a,const int&b){return (ll)a*b%mod;}
inline void Add(int&a,const int&b){a=a+b>=mod?a+b-mod:a+b;}
inline void Dec(int&a,const int&b){a=a>=b?a-b:a-b+mod;}
inline void Mul(int&a,const int&b){a=(ll)a*b%mod;}
inline int ksm(int a,int p){int ret=1;for(;p;p>>=1,a=mul(a,a))if(p&1)Mul(ret,a);return ret;}
struct Mat{
int a[64][64],n,m;
Mat(int k=0,int n0=64,int m0=64){
n=n0,m=m0;
for(ri i=0;i<n0;++i)for(ri j=0;j<m0;++j)a[i][j]=i==j?k:0;
}
friend inline Mat operator+(const Mat&a,const Mat&b){
Mat ret(0,a.n,b.m);
for(ri i=0;i<a.n;++i)for(ri j=0;j<a.m;++j)ret.a[i][j]=add(a.a[i][j],b.a[i][j]);
return ret;
}
friend inline Mat operator*(const Mat&a,const Mat&b){
Mat ret(0,a.n,b.m);
for(ri i=0;i<a.n;++i)for(ri k=0;k<a.m;++k)if(a.a[i][k])
for(ri j=0;j<b.m;++j)Add(ret.a[i][j],mul(a.a[i][k],b.a[k][j]));
return ret;
}
friend inline Mat operator^(Mat a,int p){
Mat ret=a;
for(--p;p;p>>=1,a=a*a)if(p&1)ret=ret*a;
return ret;
}
}pw[32],ban[4];
typedef pair<int,int> pii;
const int N=1005;
int n,a[N],f[2][4],cur=0,tmp[4],sum[4];
vector<pii>upd[N];
bool Ban[4];
inline void update(int x,Mat&t){for(ri i=31;~i;--i)if((x>>i)&1)t=pw[i]*t;}
int main(){
n=read();
for(ri i=1;i<=n;++i)a[i]=read();
for(ri a,b,c,tt=read();tt;--tt){
a=read(),b=read(),c=read();
upd[a].push_back(pii(b,c));
}
for(ri tt=1;tt<4;++tt){
for(ri i=1;i<4;++i)Ban[i]=(bool)read();
for(ri s1,s2,s3,s4,sta=0;sta<64;++sta){
s1=sta&3,s2=(sta>>2)&3,s3=(sta>>4)&3,s4=0;
for(ri i=0;i<4;++i)tmp[i]=0;
Ban[3]&&(tmp[s1]=1),Ban[2]&&(tmp[s2]=1),Ban[1]&&(tmp[s3]=1);
while(tmp[s4])++s4;
ban[tt].a[s2|(s3<<2)|(s4<<4)][sta]=1;
}
}
pw[0]=ban[1]+ban[2]+ban[3];
for(ri i=1;i<32;++i)pw[i]=pw[i-1]*pw[i-1];
f[cur=0][0]=1;
for(ri pre,tt=1;tt<=n;++tt){
sort(upd[tt].begin(),upd[tt].end());
Mat t(0,64,1);
t.a[63][0]=1;
pre=0;
for(ri pos,col,i=0;i<upd[tt].size();++i){
pos=upd[tt][i].fi,col=upd[tt][i].se;
update(pos-1-pre,t);
t=ban[col]*t;
pre=pos;
}
update(a[tt]-pre,t);
for(ri i=0;i<4;++i)sum[i]=0;
for(ri i=0;i<64;++i)Add(sum[(i>>4)&3],t.a[i][0]);
cur^=1;
for(ri i=0;i<4;++i){
f[cur][i]=0;
for(ri j=0;j<4;++j)Add(f[cur][i],mul(f[cur^1][j],sum[i^j]));
}
}
cout<<f[cur][0];
return 0;
}