2021 ccpcf vp
A(模拟)
按题意模拟至指令达到上限
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,m,now,g[MAXN];
int main()
{
n=read(),m=read();
char ch[10];
rep(i,1,n)
{
scanf("%s",ch+1);getchar();
if(ch[1]=='e') g[i]='a'-getchar();
else g[i]=read();
}
int now=1;
for(;now<=m&&n<=m;now++)
{
if(g[now]>0) rep(i,1,min(g[now],m-n)) g[++n]=g[i];
else putchar('a'-g[now]);
}
for(;now<=m;now++) if(g[now]<=0) putchar('a'-g[now]);
puts("");
}
C(猜结论)
倒着推找规律,每次相当于在上一次的交换序列(\(c[i+1]+1\)个数)上选出\(c[i]\)个数做本次的交换序列
即每次\(ans*=\binom{c[i+1]+1}{c[i]}\)
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
const int MOD=998244353;
int n,fac[MAXN],ifac[MAXN],g[MAXN];
int qp(int x,int t,int res=1)
{
for(;t;t>>=1,x=1LL*x*x%MOD)
if(t&1) res=1LL*res*x%MOD;
return res;
}
inline int C(int n,int m)
{
return ((1LL*fac[n]*ifac[m])%MOD*ifac[n-m])%MOD;
}
int main()
{
n=read();
fac[0]=1;rep(i,1,n) fac[i]=(1LL*i*fac[i-1])%MOD;
ifac[n]=qp(fac[n],MOD-2);
dwn(i,n-1,0) ifac[i]=(1LL*(i+1)*ifac[i+1])%MOD;
rep(i,1,n-1) g[i]=read();
int las=1,ans=1;
dwn(i,n-1,1)
{
ans=1LL*ans*C(las,g[i])%MOD;
las=g[i]+1;
}
cout<<ans<<endl;
}
E(构造)
考虑构造在最上层构造如图所示的图形
注意宽度为偶数且仅最右侧有块时需要翻转
#include<bits/stdc++.h>
using namespace std;
const int Maxn=10005;
int w,n;
char ssr[Maxn];
int main() {
scanf("%d%d",&w,&n);
cout<<w<<'\n';
int stp=0;
for(int t=1;t<=n;++t) {
scanf("%s",ssr+1);
for(int i=1;i<=w;++i) {
if(ssr[i]=='#') {
stp=i; break;
}
}
if(stp) break;
}
if(!stp) stp=1;
if(w&1) {
if(stp==w) stp-=2;
else stp-=(1-(stp&1));
cout<<"T 2 "<<stp<<'\n';
for(int i=stp-2;i>0;i-=2) {
cout<<"S 0 "<<i<<'\n';
}
cout<<"T 3 1"<<'\n';
for(int i=stp+2;i+2<=w;i+=2) {
cout<<"Z 0 "<<i<<'\n';
}
for(int i=2;i+2<=w;i+=2) {
cout<<"Z 0 "<<i<<'\n';
}
cout<<"L 2 "<<w-1<<'\n';
}
else {
if(stp==w) {
stp-=2;
cout<<"T 2 "<<stp<<'\n';
for(int i=stp-2;i>0;i-=2) {
cout<<"S 0 "<<i<<'\n';
}
cout<<"T 3 "<<1<<'\n';
cout<<"T 1 "<<w-1<<'\n';
for(int i=w-3;i>1;i-=2) {
cout<<"S 0 "<<i<<"\n";
}
cout<<"T 0 1\n";
}
else {
stp-=(1-(stp&1));
if(stp+2>w) stp-=2;
cout<<"T 2 "<<stp<<'\n';
for(int i=stp-2;i>0;i-=2) {
cout<<"S 0 "<<i<<'\n';
}
cout<<"T 3 1"<<'\n';
for(int i=stp+2;i+2<=w;i+=2) {
cout<<"Z 0 "<<i<<'\n';
}
cout<<"T 1 "<<w-1<<'\n';
for(int i=2;i+2<w;i+=2) {
cout<<"Z 0 "<<i<<'\n';
}
cout<<"T 0 "<<w-2<<'\n';
}
}
return 0;
}
F(枚举)
显然有意义的模数序列需单调递减,因此排序后\(2^n\)枚举即可
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
const int MAXN=200100;
const int inf=1e9;
ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n;
ll a[25],now,ans;
int main()
{
n=read();
rep(i,0,n-1) a[i]=read();
now=read();
sort(a,a+n);
reverse(a,a+n);
n--;
rep(st,0,(1<<n)-1)
{
ll x=now;
rep(i,0,n-1) if(st>>i&1)
x%=a[i];
ans=max(ans,x%a[n]);
}
cout<<ans<<endl;
}
G(sg函数)
对每一个\([l,r]\)作为子游戏,使用sg函数
令\(sg(l,l-1)=0\),容易得到\(sg(l,l)=1,sg(l,l+1)=2,\cdots , sg(l,lp)=lp-l+1\),而\([l,lp+1]\)的后继中不包含\([l,l-1]\),故\(sg(l,lp+1)=0\)
可以得到结论重复值总是在\([l,lp+1+kp],k\in \mathbb{N}\)出现,\(sg(l,lp+1+kp)=sg(l,l+k-1)\),其他位置递增,直接递归求解
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,ans=0;
ll p;
int calc(ll l,ll r)
{
if(r<=l*p) return r-l+1;
if((r-1)%p==0) return calc(l,(r-1)/p-1);
return r-l+1-((r-1)/p-l+1);
}
void solve()
{
n=read(),p=read(),ans=0;
rep(i,1,n)
{
int l=read(),r=read();
ans^=calc(l,r);
}
puts(ans?"First":"Second");
}
int main()
{
rep(T,1,read()) solve();
}
I(贪心+线段树)
暂不考虑翻转,假设断点为\(i\),则答案为\(sum0_i+(sum1_n-sum1_i)=sum1_n+sum_i\),其中\(sum_i\)为视0为1,1为-1的前缀和数组,则问题为求\(max(sum_i)\)
考虑翻转的操作,相当于把一段区间放到前面去;则原问题转化为在正负交替的数列中求权值最大的k个区间,可以免费选一个前缀
考虑贪心,首先选择一个最大前缀,将前缀内的数取反,之后每次找到最大区间然后将其取反
为此需要建立线段树维护区间内的最大区间,并支持查询区间位置与区间取反;为此还需要额外维护所有最小值
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;++i)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;--i)
#define ren for(int i=fst[x];i;i=nxt[i])
#define Fill(a,x) memset(a,x,sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef long double ld;
typedef unsigned long long ull;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MOD=998244353;
const int MAXN=200100;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline ll readll()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace CALC
{
inline int pls(int a,int b){return a+b>=MOD?a+b-MOD:a+b;}
inline int mns(int a,int b){return a-b<0?a-b+MOD:a-b;}
inline int mul(int a,int b){return (1LL*a*b)%MOD;}
inline void inc(int &a,int b){a=pls(a,b);}
inline void dec(int &a,int b){a=mns(a,b);}
inline void tms(int &a,int b){a=mul(a,b);}
inline int qp(int x,int t,int res=1)
{for(;t;t>>=1,x=mul(x,x)) if(t&1) res=mul(res,x);return res;}
inline int Inv(int x){return qp(x,MOD-2);}
}
using namespace CALC;
const ll inf=1e18;
int n,g[MAXN],tag[MAXN<<2];
ll ans[MAXN];
struct Seg{int l,r;ll w;Seg(int l=0,int r=0,ll w=0):l(l),r(r),w(w){}};
Seg operator + (const Seg &a,const Seg &b){return Seg(a.l,b.r,a.w+b.w);}
Seg operator - (const Seg &a){return Seg(a.l,a.r,-a.w);}
bool operator < (const Seg &a,const Seg &b){return a.w<b.w;}
struct node
{
Seg sum,mx,mn,rmx,rmn,lmx,lmn;
void rev()
{
static Seg tmp;
sum=-sum;
tmp=mx,mx=-mn,mn=-tmp;
tmp=rmx,rmx=-rmn,rmn=-tmp;
tmp=lmx,lmx=-lmn,lmn=-tmp;
}
}tr[MAXN<<2];
node operator + (const node &a,const node &b)
{
static node res;
res.sum=a.sum+b.sum;
res.mx=max({a.mx,b.mx,a.rmx+b.lmx});
res.mn=min({a.mn,b.mn,a.rmn+b.lmn});
res.rmx=max(b.rmx,a.rmx+b.sum);
res.rmn=min(b.rmn,a.rmn+b.sum);
res.lmx=max(a.lmx,a.sum+b.lmx);
res.lmn=min(a.lmn,a.sum+b.lmn);
return res;
}
void build(int k,int l,int r)
{
if(l==r)
{
tr[k].sum=tr[k].mx=tr[k].mn=Seg(l,r,g[l]);
tr[k].rmx=tr[k].rmn=tr[k].lmn=tr[k].lmx=tr[k].sum;
return ;
}
int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
tr[k]=tr[k<<1]+tr[k<<1|1];
}
inline void pshd(int k)
{
tr[k<<1].rev();tr[k<<1|1].rev();
tag[k<<1]^=1,tag[k<<1|1]^=1;
tag[k]=0;
}
void mdf(int k,int l,int r,int a,int b)
{
if(a<=l&&r<=b){tag[k]^=1;tr[k].rev();return ;}
int mid=l+r>>1;if(tag[k]) pshd(k);
if(a<=mid) mdf(k<<1,l,mid,a,b);
if(b>mid) mdf(k<<1|1,mid+1,r,a,b);
tr[k]=tr[k<<1]+tr[k<<1|1];
}
int main()
{
n=read();
ll premx=0,id=-1,sum=0,bas=0;
rep(i,1,n)
{
g[i]=!read()?1:-1;g[i]*=read();
sum+=g[i];
if(sum>premx) id=i,premx=sum;
if(g[i]<0) bas-=g[i];
}
build(1,1,n);
ans[0]=premx;
if(id!=-1) mdf(1,1,n,1,id);
int k=1;
for(;tr[1].mx.w>0;k++)
{
ans[k]=ans[k-1]+tr[1].mx.w;
mdf(1,1,n,tr[1].mx.l,tr[1].mx.r);
}
rep(Q,1,read())
{
int x=min(read(),k-1);
printf("%lld\n",ans[x]+bas);
}
}
J(折半搜索)
枚举所有前五题的答案,得到若干长度为\(n\)的得分向量;只需后五题的得分向量与该向量相加为原得分序列即可
因为对于每个前五题答案得到的向量,计入哈希表中;枚举后五题答案后在哈希表中查找互补向量的个数
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
int n,g[20010][11],a[15],w[20010];
char s[15];
unordered_map<ll,int> cnt;
const int mod1=1000000009,bas1=233,mod2=998244353,bas2=137;
int cheq1(int i,int res=0){rep(j,1,5) res+=(g[i][j]==a[j]);return res;}
int cheq2(int i,int res=0){rep(j,6,10) res+=(g[i][j]==a[j]);return res;}
void work()
{
ll x=0,y=0;
rep(i,1,n)
{
int t=cheq1(i);
if(t>w[i]) return ;
t=w[i]-t;
x=(x*bas1+t+1)%mod1,y=(y*bas2+t+1)%mod2;
}
cnt[(x<<31)+y]++;
}
int calc()
{
ll x=0,y=0;
rep(i,1,n)
{
int t=cheq2(i);
x=(x*bas1+t+1)%mod1,y=(y*bas2+t+1)%mod2;
}
return cnt[(x<<31)+y];
}
void solve()
{
n=read();
cnt.clear();
rep(i,1,n)
{
scanf("%s",s+1);w[i]=read()/10;
rep(j,1,10) g[i][j]=s[j]-'A'+1;
}
cnt.clear();
for(a[1]=1;a[1]<5;a[1]++)
for(a[2]=1;a[2]<5;a[2]++)
for(a[3]=1;a[3]<5;a[3]++)
for(a[4]=1;a[4]<5;a[4]++)
for(a[5]=1;a[5]<5;a[5]++) work();
int ans=0;
for(a[6]=1;a[6]<5;a[6]++)
for(a[7]=1;a[7]<5;a[7]++)
for(a[8]=1;a[8]<5;a[8]++)
for(a[9]=1;a[9]<5;a[9]++)
for(a[10]=1;a[10]<5;a[10]++) ans+=calc();
printf("%d\n",ans);
}
int main()
{
rep(T,1,read()) solve();
}
K(分类讨论)
不妨令\(n<m\),对\(n=1\)的情况单独讨论,否则考虑以下几种情况:
- 只选一个点,只有四个角满足,\(ans+=4\)
- 选两个点,需两个点分别在同侧边或对边上,\(ans+=\binom{2n-4}{2}+\binom{2m-4}{2}\)
- 选三个点时需继续讨论
-
- 存在两个点在边上时,需两点在邻边上,内部任取一个点即可。\(ans+=4(n-2)^2(m-2)^2\)
-
- 仅有一个点在边上时,只有当内部的的两个点分别位于该点两侧且该方向的坐标差与边上点\(>1\)时不合法,即全集为\(ans+=(2n+2m-8)\binom{(n-2)(m-2))}{4}\)。其中一种不合法的方案数为\((m-2)^2\sum\limits_{i=4}^{n-3}i(n-7-i)\)化简为平方和与一次和的形式,另一种方案只需交换\(m,n\)同理
- 选四个点,需四个点均不在边上,否则对于边上的点一定存在一侧有两个点,可以被删去。\(ans+=\binom{(n-2)(m-2)}{4}\)
#include<bits/stdc++.h>
#define rep(i,s,t) for(int i=(s),i##end=(t);i<=i##end;i++)
#define dwn(i,s,t) for(int i=(s),i##end=(t);i>=i##end;i--)
using namespace std;
typedef long long ll;
typedef long double ld;
typedef double db;
typedef vector<int> vi;
typedef pair<int,int> pii;
const int MAXN=200100;
const int inf=1e9;
int read()
{
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-'0',ch=getchar();}
return x*f;
}
const int MOD=998244353;
int n,m;
int qp(int x,int t,int res=1)
{
for(;t;t>>=1,x=1LL*x*x%MOD)
if(t&1) res=1LL*res*x%MOD;
return res;
}
const int ifac[]={1,1,qp(2,MOD-2),qp(6,MOD-2),qp(24,MOD-2)};
int C(int n,int m)
{
ll res=ifac[m];
dwn(i,n,n-m+1) (res*=i)%=MOD;
return res;
}
int s2(int n){return (((1LL*n*(n+1)%MOD)*(2*n+1))%MOD)*ifac[3]%MOD;}
inline int sqr(int x){return 1LL*x*x%MOD;}
int solve()
{
n=read(),m=read();
if(n>m) swap(n,m);
if(n==1)
{
if(m<=2) return m;
return (2+C(m-2,2))%MOD;
}
int ans=4;
(ans+=(C(2*n-4,2)+C(2*m-4,2))%MOD)%=MOD;
(ans+=(4LL*sqr(n-2)%MOD)*sqr(m-2)%MOD)%=MOD;
(ans+=C(1LL*(n-2)*(m-2)%MOD,4))%=MOD;
(ans+=2LL*C(1LL*(n-2)*(m-2)%MOD,2)*(n+m-4)%MOD)%=MOD;
rep(i,1,2)
{
if(n>=7)
{
int res=1LL*C(n-5,2)*(n-5)%MOD;
(res+=MOD-s2(n-6))%=MOD;
(ans+=MOD-(2LL*sqr(m-2)*res%MOD))%=MOD;
}
swap(n,m);
}
return ans;
}
int main()
{
rep(T,1,read()) printf("%d\n",solve());
}
L(贪心)
显然休息的越靠后一定不会使答案变劣,以此策略模拟即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n, m, X, Y, p[200011], a[200011];
int main()
{
int i;
scanf("%lld%lld%lld%lld", &n, &m, &X, &Y);
for (i=1; i<=m; ++i)
{
scanf("%lld", &p[i]);
a[i] = p[i] - p[i-1] - 1;
}
a[m+1] = n - p[m];
ll ans = 0, pre = 0;
for (i=1; i<=m+1; ++i)
{
ll now = min(Y - pre, X);
// now, 1, Y-now, 1
ll t = a[i] / (Y + 2);
ll lft = a[i] - t * (Y + 2);
if (lft <= now)
{
ans += 2 * t;
pre = lft;
}
else if (lft == now+1)
{
ans += 2*t+1;
pre = 0;
}
else if (lft <= Y + 1)
{
ans += 2*t+1;
pre = lft - now - 1;
}
else
{
assert(0);
}
}
printf("%lld\n", ans);
return 0;
}