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';
}
posted @ 2022-09-19 21:33  jack_yyc  阅读(305)  评论(0编辑  收藏  举报