2022icpc网络赛(I)
A(预处理)
容易发现对于一段被0隔开的长度为\(n\)的连续的1,可以消去的0的个数为\(\lceil\frac{n}{2}\rceil\),预处理这个答案
同时因为每次查询一个区间视为环形,需要将两个端点拼起来,预处理每个位置向左向右最远延伸到1的位置即可
#include<bits/stdc++.h>
using namespace std;
const int Maxn=1000005;
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,Q;
int a[Maxn];
char ssr[Maxn];
int preans[Maxn],nxt[Maxn],pre[Maxn];
int main() {
n=read(); Q=read();
scanf("%s",ssr+1);
for(int i=1;i<=n;++i) {
a[i]=ssr[i]-'0';
}
int cnt=0;
for(int i=1;i<=n;++i) {
preans[i]=preans[i-1];
if(a[i]==1) {
++cnt;
if(cnt&1) ++preans[i];
}
else cnt=0;
}
int last=0;
for(int i=1;i<=n;++i) {
if(a[i]==1) {
if(last==0) last=i;
pre[i]=last;
}
else last=0;
}
last=0;
for(int i=n;i;--i) {
if(a[i]==1) {
if(last==0) last=i;
nxt[i]=last;
}
else last=0;
}
for(int i=1;i<=Q;++i) {
int L=read(),R=read();
int cntL=L,cntR=R;
int tot=0;
if(a[L]==1&&a[R]==1) {
cntL=nxt[L];
cntR=pre[R];
int lenL=nxt[L]-L+1,lenR=R-pre[R]+1;
tot+=(lenL+lenR+1)/2;
}
else if(a[L]==1) {
cntL=nxt[L];
int lenL=nxt[L]-L+1;
tot+=(lenL+1)/2;
}
else if(a[R]==1) {
cntR=pre[R];
int lenR=R-pre[R]+1;
tot+=(lenR+1)/2;
}
if(cntL>=cntR) {
cout<<0<<'\n';
continue;
}
tot+=(preans[cntR-1]-preans[cntL]);
int output=(R-L+1)/3-tot;
cout<<max(output,0)<<'\n';
}
return 0;
}
C(结论/签到)
容易发现只有叶子节点需要delete操作,其他总可以被shrink掉(注意特判n=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;
const int MAXN=1001001;
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,deg[MAXN];
void solve()
{
cin>>n;
if(n==1) {puts("1");return ;}
for(int i=1;i<=n;++i) deg[i]=0;
for(int i=1;i<n;++i)
{
int a,b;
scanf("%d%d",&a,&b);
deg[a]++,deg[b]++;
}
int ans=0;
for(int i=1;i<=n;++i) ans+=(deg[i]==1);
printf("%d\n",ans);
}
int main()
{
int T;
cin>>T;
while(T--) solve();
}
D(打表)
打表发现这种数很少,直接dfs找出所有数即可
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
int aa[1000011];
int now, n;
void setx(int p, int x)
{
if (x == 0)
now &= ( ~(1 << p) );
else
now |= (1 << p);
}
void dfs(int p, int q)
{
if (p >= 30)
{
if (q == 0) aa[++n] = now;
return;
}
if (q >= 1)
{
setx(p, 1);
dfs(p+1, q-1);
}
if ( (30 - p) > q)
{
setx(p, 0);
dfs(p+1, q);
}
}
int main()
{
int i, j, T;
for (i=1; i<=30; ++i)
{
for (j=0; j<i; ++j)
setx(j, 0);
setx(i, 1);
dfs(i+1, i-1);
}
sort(aa+1, aa+n+1);
while (aa[n] >= 1000000000) --n;
scanf("%d", &T);
while (T--)
{
int l, r;
scanf("%d%d", &l, &r);
int t = aa[lower_bound(aa+1, aa+n+1, l) - aa];
printf("%d\n", t <= r ? t : -1);
}
return 0;
}
F(min25筛)
多校原题的子集:即求\(\mathbb{1}\)狄利克雷卷积\(k-1\)次后的前缀和
差不多是\(min25\)裸题,其中\(f(p)=k\),\(f(p^c)=\binom{k-1+c}{c}\),\(G(n)=k(n-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;
const int MAXN=200100;
const int MOD=1000000007;
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 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);}
int pri[MAXN],ptot,lpf[MAXN],inv[70],ifac[70],A,prod[70];
void sieve(int n,int m)
{
rep(i,2,n)
{
if(!lpf[i]) pri[++ptot]=i,lpf[i]=ptot;
for(int j=1;j<=lpf[i]&&i*pri[j]<=n;++j) lpf[i*pri[j]]=j;
}
inv[1]=1;rep(i,2,m) inv[i]=MOD-mul(MOD/i,inv[MOD%i]);
ifac[0]=1;rep(i,1,m) ifac[i]=mul(ifac[i-1],inv[i]);
prod[0]=1;rep(i,1,m) prod[i]=mul(prod[i-1],A+i);
}
ll glbn,lis[MAXN];
int lim,le[MAXN],ge[MAXN],G[MAXN],Fp[MAXN],cnt;
inline ll sqr(ll x){return x*x;}
inline int idx(ll x) {return x<=lim?le[x]:ge[glbn/x];}
void init(ll n)
{
for(ll i=1,j,v;i<=n;i=n/j+1)
{
j=n/i,v=j%MOD,lis[++cnt]=j;
(j<=lim?le[j]:ge[glbn/j])=cnt;
G[cnt]=mns(v,1);
}
}
void calcFp()
{
rep(k,1,ptot)
{
int p=pri[k];ll p2=sqr(p);
for(int i=1;lis[i]>=p2;++i)
{
ll v=lis[i]/p;int id=idx(v);
dec(G[i],mns(G[id],k-1));
}
}
rep(i,1,cnt) Fp[i]=mul(A+1,G[i]);
}
inline int fpc(int c){return mul(prod[c],ifac[c]);}
int F(int k,ll n)
{
if(n<pri[k]||n<=1) return 0;
int id=idx(n),ans=mns(Fp[id],mul(k-1,A+1));
for(int i=k;i<=ptot&&sqr(pri[i])<=n;++i)
{
ll pw=pri[i],pw2=sqr(pri[i]);
for(int c=1;pw2<=n;++c,pw=pw2,pw2*=pri[i])
inc(ans,pls(mul(fpc(c),F(i+1,n/pw)),fpc(c+1)));
}
return ans;
}
int main()
{
glbn=read(),A=read()-1;
lim=sqrt(glbn+0.5);
sieve(2e5,63);
init(glbn);
calcFp();
printf("%d\n",pls(F(1,glbn),1));
}
G(dp+状态优化)
考虑朴素dp:\(f[i][A][B][C][D]\)表示前i个数,\(x_1,x_2,x_3,x_4\)分别被计算了\(A,B,C,D\)次的最大答案
容易发现合法状态需要满足\(A\ge B\ge C\ge D\)且\(Ax_1+Bx_2+Cx_3+Dx_4\le T\),这样的状态实际上很少,全部存下来之后暴力转移即可
#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=250100;
const ll inf=1e18;
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;
ll lim,x1,x2,x3,x4,f[110][MAXN],sum[110];
vi vec[110];
unordered_map<int,int> id;
inline int getw(int a,int b,int c,int d) {
return d+c*100+b*10000+a*1000000;
}
void wget(int x,int& a,int& b,int& c,int& d)
{
d=x%100,x/=100;
c=x%100,x/=100;
b=x%100,x/=100;
a=x;
}
inline void chkmx(ll &a,ll b){a=a>b?a:b;}
int main() {
n=read(),lim=read(),x1=read(),x2=read(),x3=read(),x4=read();
rep(i,0,n) rep(j,i,n-i) rep(k,j,n-i-j) rep(l,k,n-i-j-k)
if(l*x1+k*x2+j*x3+i*x4<=lim)
vec[i+j+k+l].push_back(getw(l,k,j,i));
m=0;
rep(i,0,n) m+=vec[i].size();
rep(i,1,n) sum[i]=sum[i-1]+read();
f[0][0]=0;
int tot=0;
rep(i,0,n) for(auto x:vec[i]) id[x]=tot++;
tot--;
rep(i,1,tot) f[0][i]=-inf;
rep(cur,1,n)
{
rep(i,0,cur) for(auto x:vec[i]) {
int a,b,c,d,p=id[x];
wget(x,a,b,c,d);
f[cur][p]=f[cur-1][p];
if(a>=1&&cur-1>=0) chkmx(f[cur][p],f[max(cur-2,0)][id[getw(a-1,b,c,d)]]+sum[cur]-sum[cur-1]);
if(b>=1&&cur-2>=0) chkmx(f[cur][p],f[max(cur-3,0)][id[getw(a-1,b-1,c,d)]]+sum[cur]-sum[cur-2]);
if(c>=1&&cur-3>=0) chkmx(f[cur][p],f[max(cur-4,0)][id[getw(a-1,b-1,c-1,d)]]+sum[cur]-sum[cur-3]);
if(d>=1&&cur-4>=0) chkmx(f[cur][p],f[max(cur-5,0)][id[getw(a-1,b-1,c-1,d-1)]]+sum[cur]-sum[cur-4]);
}
}
ll ans=0;
rep(i,1,m) chkmx(ans,f[n][i]);
cout<<ans<<'\n';
}
H(模拟/签到)
签到
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define fi first
#define se second
const ll md = 20220911;
pii a[1000011];
int n;
int p = 0;
ll calc(int tp)
{
ll ans = 0;
for (;;)
{
++p;
if (p >= tp) break;
if (a[p].fi == 1)
{
++ans;
}
else if (a[p].fi == 2)
{
ll t = a[a[p].se].se;
ans = (ans + calc(a[p].se) * t) %md;
}
}
return ans;
}
int st[1000011];
int main()
{
char tmp[1111];
while (scanf("%s", tmp) == 1)
{
if (tmp[0] == 'a')
{
a[++n] = {0, 0};
}
else if (tmp[0] == 'l')
{
a[++n] = {1, 0};
}
else if (tmp[0] == 'r')
{
a[++n] = {2, 0};
}
else if (tmp[0] == 'f' && tmp[1] == 'o')
{
int t;
scanf("%d", &t);
a[++n] = {3, t};
}
else if (tmp[0] == 't')
{
}
else if (tmp[0] == 'f' && tmp[1] == 'i')
{
break;
}
}
for (int i=1; i<=n; ++i)
{
if (a[i].fi == 2)
{
st[++st[0]] = i;
}
else if (a[i].fi == 3)
{
a[st[st[0]--]].se = i;
}
}
printf("%lld\n", calc(n+1));
return 0;
}
J(构造)
我们的做法是把两种适用范围不同的构造拼起来:
- 设第一次的概率为\(p\),后续的概率分别为\(\frac{1}{n-1},\frac{1}{n-2},\cdots,\frac{1}{2},1\),这样后面n-1轮的出货概率实际上是相等的,可以得到\(p\)的公式,此时需满足\(2Y<X+2\)
- 设第一次概率为\(p\),后续概率为\(\frac{p}{1-p},\frac{p}{1-2p},\cdots,\frac{p}{1-(n-2)p},1\),这样前面n-1轮的出货概率实际上是相等的,可以得到\(p\)的公式,此时公式正好满足另一部分
from fractions import Fraction
aaa=[]
tmp=input().split(' ')
X=int(tmp[0])
Y=int(tmp[1])
if 2*Y<X+2:
aaa.append(Fraction(X+2-2*Y, X))
for i in range(X-1, 0, -1):
aaa.append(Fraction(1, i))
else:
p=Fraction(2*X-2*Y, X*(X-1))
aaa.append(p)
for i in range(1, X-1):
aaa.append(Fraction(p, 1-i*p))
aaa.append(1)
for i in aaa:
print(i.numerator, i.denominator)
K(dp+状态优化)
考虑朴素的dp:\(f[i][j][k]\)表示前i轮,\(w++\)操作进行了\(j\)次,攻击力为\(k\)时赢过的最多轮次
由于攻击力可能很大,有:\(f[i][j][k]\)表示前i轮,\(w++\)操作进行了\(j\)次,赢过\(k\)轮时的最大攻击力
发现值域很小,至多只需要将\(w++\)操作进行\(\sqrt{V}\)次,否则是没有意义的;也即只需要\(2\sqrt{V}\)轮,攻击力就已经达到值域上限,因此至多输\(2\sqrt{V}\)轮
这样令\(f[i][j][k]\)表示前i轮,\(w++\)操作进行了\(j\)次,输为\(k\)时的最大攻击力即可
时间复杂度\(O(nV)\),滚动数组后空间复杂度\(S(V)\)
#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,g[5050],f[2][100][220];
void chkmx(int &a,int b){a=a>b?a:b;}
int main()
{
n=read();rep(i,1,n) g[i]=read();
memset(f,0x8f,sizeof(f));
f[0][0][0]=1;
int m=sqrt(n+0.5)+5;
rep(t,1,n)
{
int cur=t&1,las=cur^1;
rep(i,0,min(m,t)) rep(j,0,min(200,t)) f[cur][i][j]=-inf;
rep(i,0,min(m,t)) rep(j,0,min(200,t))
{
if(f[las][i][j]+i+1>=g[t]) chkmx(f[cur][i][j],f[las][i][j]+i+1);
else chkmx(f[cur][i][j+1],f[las][i][j]+i+1);
if(f[las][i][j]>=g[t]) chkmx(f[cur][i+1][j],f[las][i][j]);
else chkmx(f[cur][i+1][j+1],f[las][i][j]);
}
}
rep(res,0,200) rep(i,0,m)
if(f[n&1][i][res]>0) return printf("%d\n",n-res),0;
}
L(dp)
要满足要求,只需要\(s'\)中不出现任意\(t\)中的有序字符对即可,设\(S_{ch}\)表示字符\(ch\)后可以仍可以选择的合法字符集,根据\(t\)串可以得到,且所有\(S_{ch}\)一定满足一个连续的\(\subseteq\)关系
因为这个关系,先把串\(s\)中所有没有在\(t\)中出现过的字符拿出来,后令\(f[i][ch]\)表示串\(s\)的前\(i\)个字符,最后一个字符选择了\(ch\)的最长\(s'\)长度,按照集合关系暴力转移即可,正确性由上述的几何从属关系保证
#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=1001001;
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;
}
char s[MAXN],t[MAXN];
int n,m,cur[26],st[26],now,vis[26];
vi frm[26];
int main()
{
scanf("%s%s",s+1,t+1);
n=strlen(s+1),m=strlen(t+1);
int tot=0;
dwn(i,m,1)
{
int ch=t[i]-'a';
st[ch]=now,vis[ch]=1;
now|=1<<ch;
}
rep(x,0,25) rep(i,0,25)
{
if((st[i]>>x)&1) ;
else frm[x].push_back(i);
}
int tmp=0;
rep(p,1,n)
{
int ch=s[p]-'a';
if(!vis[ch]) {tmp++;continue;}
int mx=cur[ch];
for(auto x:frm[ch])
mx=max(mx,cur[x]+1);
cur[ch]=mx;
}
int ans=0;
rep(i,0,25) ans=max(ans,cur[i]);
cout<<ans+tmp<<'\n';
}