2023 ICPC Gran Premio de Mexico 2da Fecha

2023 ICPC Gran Premio de Mexico 2da Fecha

文章目录

  • A. Alaric Magic Partition
  • B. Bogo Sort Probability
  • C. Choose Two
  • D. Draconis Subarrays
  • E. Earnings Report
  • F. Fibonacci Fever
  • G. Guessing Two Steps into the Multiverse
  • H. How Many Groups
  • I. Iron Fist Ketil vs King Canute
  • J. JP's List of Trips
  • K. Knockout Spell
  • L. ICPC Teams
  • M. Modify the Array
  • N. Necklace

A. Alaric Magic Partition

思路:考虑贪心,即每次只需拆出一个数字,因为1,2,3,4,5,7,9都符合条件,而剩下的0,6,8这3个数无论怎么组合都无法满足条件(坑点:0不算完全平方数)。复杂度 O ( n ) O(n) O(n)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())-1
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
char s[MAX];
int solve()
{
    int n,ans=0;
    scanf("%d%s",&n,s);
    for(int i=0;i<n;i++)ans+=(s[i]!='0'&&s[i]!='6'&&s[i]!='8');
    return printf("%d\n",ans);
}
int main()
{
    int T=1;
//    cin>>T;
    while(T--)solve();
    return 0;
}

B. Bogo Sort Probability

思路:求多重集的排列数。复杂度 O ( k ∗ log ⁡ 2 1 0 9 ) O(k*\log_210^9) O(klog2109)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())-1
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll POW(ll x,ll n)
{
    ll res=1;
    while(n)
    {
        if(n&1)res=res*x%MOD;
        x=x*x%MOD;
        n>>=1;
    }return res;
}
ll fac[MAX],a[MAX];
map<int,int>c;
int solve()
{
    int n,q;
    cin>>n>>q;
    fac[1]=1;
    for(int i=2;i<=n;i++)fac[i]=fac[i-1]*i%MOD;
    ll ans=POW(fac[n],MOD-2);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",a+i);
        c[a[i]]++;
    }
    for(auto &kv:c)ans=ans*fac[kv.second]%MOD;
    printf("%lld\n",ans);
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ans=ans*POW(c[a[x]],MOD-2)%MOD;
        c[a[x]]--;
        c[y]++;
        a[x]=y;
        ans=ans*c[y]%MOD;
        printf("%lld\n",ans);
    }
    return 0;
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

C. Choose Two

思路:考虑枚举 H H H最大值。利用单调栈我们可以得到一个个分隔且互不覆盖的区间 [ L 1 , R 1 ] , [ L 2 , R 2 ] , . . . . , [ L k , R k ] [L_1,R_1],[L_2,R_2],....,[L_k,R_k] [L1,R1],[L2,R2],....,[Lk,Rk]
这些区间内的最大值均为 H H H,同时我们也知道这些区间中值为 H H H的数的下标。
于是我们可以分别求出各个区间内满足最大值为 H H H的子区间的个数 c 1 , c 2 , . . . , c k c_1,c_2,...,c_k c1,c2,...,ck
那么各区间之前相互组合满足答案的方案数为
s = c 1 ∗ ( c 2 + c 3 + . . . + c k ) + c 2 ∗ ( c 3 + . . . + c k ) + . . . + c k − 1 ∗ c k \begin{aligned} s=&c_1*(c_2+c_3+...+c_k)\\ &+c_2*(c_3+...+c_k)\\ &+...\\ &+c_{k-1}*c_k \end{aligned} s=c1(c2+c3+...+ck)+c2(c3+...+ck)+...+ck1ck
接下来只需求出各个区间内组合满足条件的方案数 s i s_i si,并累计至答案即可。
s i s_i si,考虑用后缀和统计好满足条件的区间数,然后遍历区间的最大值 H H H的下标 x x x,将 [ L i , R i ] [L_i,R_i] [Li,Ri]分成2半 [ L i , x ] , [ x , R i ] [L_i,x],[x,R_i] [Li,x],[x,Ri],并利用前缀和方案数和后缀和方案数计算(用等差数列计算)出符合条件的不覆盖方案数。
复杂度 O ( n ) O(n) O(n)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=2e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
vector<int>p[MAX];
int h[MAX],L[MAX],R[MAX];
int init(int n)
{
    stack<int>s;
    for(int i=1;i<=n;i++)
    {
        p[h[i]].push_back(i);
        while(!s.empty()&&h[s.top()]<=h[i])s.pop();
        L[i]=(s.empty()?0:s.top())+1;
        s.push(i);
    }
    while(!s.empty())s.pop();
    for(int i=n;i>=1;i--)
    {
        while(!s.empty()&&h[s.top()]<=h[i])s.pop();
        R[i]=(s.empty()?n:(s.top()-1));
        s.push(i);
    }
    return 0;
}
ll cal(int m)
{
    ll sum=0;
    ll pre=0;
    for(int i=0;i<sz(p[m]);)
    {
        ll l=L[p[m][i]];
        ll r=R[p[m][i]];
        vector<ll> tmp;
        while(i<sz(p[m])&&p[m][i]<=r)
        {
            tmp.push_back(p[m][i]);
            i++;
        }
        ll ex=0;
        for(int j=0;j+1<sz(tmp);j++)(ex+=((tmp[j+1]-tmp[j])*(tmp[j+1]-tmp[j]-1)/2)%MOD)%=MOD;
        ex+=((tmp[0]-l+1)*(tmp[0]-l)/2)%MOD;
        ex+=((r-tmp.back()+1)*(r-tmp.back())/2)%MOD;
        ex%=MOD;

        ll now=(((r-l+1+1)*(r-l+1)/2)%MOD-ex+MOD)%MOD;
        (sum+=now*pre%MOD)%=MOD;
        (pre+=now)%=MOD;

        ll suf=0;
        for(int j=0;j<sz(tmp);j++)(suf+=(tmp[j]-(j==0?l-1:tmp[j-1]))*(r-tmp[j]+1)%MOD)%MOD;
        for(int j=0;j+1<sz(tmp);j++)
        {
            suf-=(tmp[j]-(j==0?l-1:tmp[j-1]))*(r-tmp[j]+1)%MOD;
            suf=(suf%MOD+MOD)%MOD;
            ll k=tmp[j]-l+1;
            ll d=r-tmp[j+1]+1;
            ll c=tmp[j+1]-tmp[j]-1;
            sum+=k*((c+1)*suf%MOD-(c*d*(c+1)/2)%MOD+MOD)%MOD;
            sum%=MOD;
        }
    }
    return sum;
}
int solve()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)scanf("%d",h+i);
    init(n);
    ll ans=0;
    for(int i=1;i<=n;i++)ans=(ans+cal(i))%MOD;
    printf("%lld\n",ans);
    return 0;
}
int main()
{
    int T=1;
    //cin>>T;
    while(T--)solve();
    return 0;
}

D. Draconis Subarrays

思路:分别求出差分数组,然后用KMP统计数量即可。(坑点:要用快读,不然会T)。复杂度 O ( n + m ) O(n+m) O(n+m)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())-1
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
void read(int &x)
{
    x=0;
    int f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x*=f;
}
int a[MAX],b[MAX],f[MAX];
int solve()
{
    int m,n;
    read(m);
    read(n);
    for(int i=1;i<=m;i++)read(a[i]);
    for(int i=1;i<=n;i++)read(b[i]);
    if(m==1)return printf("%d\n",n);
    for(int i=0;i<=m-2;i++)a[i]=a[i+2]-a[i+1];
    m--;
    for(int i=0;i<=n-2;i++)b[i]=b[i+2]-b[i+1];
    n--;
    f[0]=f[1]=0;
    for(int i=1;i<m;i++)
    {
        int j=f[i];
        while(j&&a[i]!=a[j])j=f[j];
        f[i+1]=(a[i]==a[j]?j+1:0);
    }
    int ans=0;
    for(int i=0,j=0;i<n;i++)
    {
        while(j&&b[i]!=a[j])j=f[j];
        if(b[i]==a[j])j++;
        if(j==m)
        {
            ans++;
            j=f[j];
        }
    }
    return printf("%d\n",ans);
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

E. Earnings Report

思路:分三种类型,求出每种类型发薪日下标区间 [ l , r ] [l,r] [l,r],并在区间端点处打上标记,利用前缀和求出累计和即可。查询时直接利用提前计算好的前缀和计算。
复杂度 O ( 10000 ∗ 366 ) O(10000*366) O(10000366)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=2e5+10;
const int MOD=998244353;
const int N=3000000;
const double PI=acos(-1.0);
typedef long long ll;
int md[10000*12];
int days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
struct Date{
    int y,m,d;
    static Date PreDay(Date d)
    {
        if(d.d>1){d.d--;return d;}
        if(d.m>1){d.m--;d.d=Monthdays(d);return d;}
        d.y--,d.m=12,d.d=31;
        return d;
    }
    static int Monthdays(int y,int m){return days[m-1]+(m==2)*((y%4==0&&y%100!= 0)||y%400==0);}
    static int Monthdays(Date d){return days[d.m-1]+(d.m==2)*((d.y%4==0&&d.y%100!= 0)||d.y%400==0);}

    static int Days(Date d){return md[(d.y-2000)*12+d.m-1]+d.d;}
    static int FloorWeeks(Date d){return Days(d)/7;}
    static int CeilWeeks(Date d){return (Days(d)+6)/7;}
    static int FloorMonths(Date d){return (d.y-2000)*12+d.m-1+(d.d>=Monthdays(d));}
    static int CeilMonths(Date d){return (d.y-2000)*12+d.m;}
    static int FloorHalfMonths(Date d){return ((d.y-2000)*12+d.m-1)*2+(d.d>=15)+(d.d>=Monthdays(d));}
    static int CeilHalfMonths(Date d){return ((d.y-2000)*12+d.m-1)*2+1+(d.d>=16);}
};
void init()
{
    memset(md,0,sizeof md);
    for(int y=2000;y<=10000;y++)
    for(int m=1;m<=12;m++)md[(y-2000)*12+m]=Date::Monthdays(y,m);
    for(int m=1;m<=8001*12;m++)md[m]+=md[m-1];
}
ll d[3][N];
int solve()
{
    auto read=[]()->Date{
        char s[20];
        scanf("%s",s);
        if(s[0]=='N')return {9999,12,31};
        return {(s[6]-'0')*1000+(s[7]-'0')*100+(s[8]-'0')*10+s[9]-'0',
        (s[3]-'0')*10+s[4]-'0',
        (s[0]-'0')*10+s[1]-'0'};
    };
    memset(d,0,sizeof d);
    int n,q;
    cin>>n>>q;
    while(n--)
    {
        int money=0,be=0,ee=0,tp=0;;
        scanf("%d",&money);
        auto b=read();
        auto e=read();
        char s[20];
        scanf("%s",s);
        if(s[0]=='w') {
            tp=0;
            be=Date::CeilWeeks(b);
            ee=Date::FloorWeeks(e);
        } else if(s[0]=='b') {
            tp=1;
            be=Date::CeilHalfMonths(b);
            ee=Date::FloorHalfMonths(e);
        } else {
            tp=2;
            be=Date::CeilMonths(b);
            ee=Date::FloorMonths(e);
        }
        d[tp][be]+=money;
        d[tp][ee+1]-=money;
    }
    for(int i=0;i<3;i++)
    for(int j=1;j<N;j++)d[i][j]+=d[i][j-1];

    for(int i=0;i<3;i++)
    for(int j=1;j<N;j++)d[i][j]+=d[i][j-1];
    while(q--)
    {
        auto b=read();
        auto e=read();
        ll ans=0;
        if(b.y==2000&&b.m==1&&b.d==1)
        {
            ans+=d[0][Date::FloorWeeks(e)];
            ans+=d[1][Date::FloorHalfMonths(e)];
            ans+=d[2][Date::FloorMonths(e)];
        }
        else
        {
            b=Date::PreDay(b);
            ans+=d[0][Date::FloorWeeks(e)]-d[0][Date::FloorWeeks(b)];
            ans+=d[1][Date::FloorHalfMonths(e)]-d[1][Date::FloorHalfMonths(b)];
            ans+=d[2][Date::FloorMonths(e)]-d[2][Date::FloorMonths(b)];
        }
        printf("%lld\n",ans);
    }
    return 0;
}
int main()
{
    init();
    int T=1;
    while(T--)solve();
    return 0;
}

F. Fibonacci Fever

思路:求Fibonacci数列的幂次和,原题且网上有很多题解。
即利用Fibonacci的通项公式
F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n] Fn=5 1[(21+5 )n(215 )n]
C = 1 5 , A = 1 + 5 2 , B = 1 − 5 2 C=\frac{1}{\sqrt5},A=\frac{1+\sqrt5}{2},B=\frac{1-\sqrt5}{2} C=5 1,A=21+5 ,B=215
a n s = F 1 k + F 2 k + . . . + F n k = C k ( A − B ) k + C k ( A 2 − B 2 ) k + . . . + C k ( A n − B n ) k = C k ( ∑ i = 0 k ( − 1 ) i A i B k − i + ∑ i = 0 k ( − 1 ) i A 2 i B 2 ( k − i ) + . . . + ∑ i = 0 k ( − 1 ) i A n i B n ( k − i ) ) \begin{aligned} ans&=F_1^k+F_2^k+...+F_n^k\\ &=C^k(A-B)^k+C^k(A^2-B^2)^k+...+C^k(A^n-B^n)^k\\ &=C^k(\sum_{i=0}^k(-1)^iA^iB^{k-i}+\sum_{i=0}^k(-1)^iA^{2i}B^{2(k-i)}+...+\sum_{i=0}^k(-1)^iA^{ni}B^{n(k-i)}) \end{aligned} ans=F1k+F2k+...+Fnk=Ck(AB)k+Ck(A2B2)k+...+Ck(AnBn)k=Ck(i=0k(1)iAiBki+i=0k(1)iA2iB2(ki)+...+i=0k(1)iAniBn(ki))
如上式所示, ( − 1 ) i A i B k − i , ( − 1 ) i A 2 i B 2 ( k − i ) , . . . , ( − 1 ) i A n i B n ( k − i ) (-1)^iA^iB^{k-i},(-1)^iA^{2i}B^{2(k-i)},...,(-1)^iA^{ni}B^{n(k-i)} (1)iAiBki,(1)iA2iB2(ki),...,(1)iAniBn(ki)构成了一个等比数列。
其中等比 q i = A i B k − i q_i=A^iB^{k-i} qi=AiBki,首项 a 1 = ( − 1 ) i A i B k − i a_1=(-1)^iA^iB^{k-i} a1=(1)iAiBki。则
a n s = C k ∑ i = 0 k a i q i ( q i n − 1 ) q i − 1 ans=C^k\sum_{i=0}^k a_i\frac{q_i(q_i^n-1)}{q_i-1} ans=Cki=0kaiqi1qi(qin1)
接下来只需分别求出 C k , A i , B k − i 对 1 0 9 + 7 C^k,A^i,B^{k-i}对10^9+7 Ck,Ai,Bki109+7取模后的值,然后用于计算即可。
A , B , C A,B,C A,B,C均带有 5 \sqrt{5} 5 ,如果 5 5 5 1 0 9 + 7 10^9+7 109+7的二次剩余,则我们求出平方同于 5 5 5的整数解,用来代替 5 \sqrt{5} 5 计算即可。但 5 5 5不是 1 0 9 + 7 10^9+7 109+7的二次剩余。

考虑利用复数的性质将 A , B , C A,B,C A,B,C转化为复数的形式来计算,其中虚部带根号,实部不带根号。即
A = 1 2 + 1 2 i B = 1 2 − 1 2 i C = 1 2 i \begin{aligned} A&=\frac12+\frac12i\\ B&=\frac12-\frac12i\\ C&=\frac12i \end{aligned} ABC=21+21i=2121i=21i
其中 i = 5 , i 2 = 5 i=\sqrt{5},i^2=5 i=5 ,i2=5
因为最终答案一定是整数,所以只需输出最终虚数的实部即可。
复杂度 O ( k ∗ log ⁡ 2 n ) O(k*\log_2n) O(klog2n)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=2e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll Inv(ll x) {
    ll ret=1;
    for(int n=MOD-2;n;n>>=1){
        if(n&1)ret=ret*x%MOD;
        x=x*x%MOD;
    }return ret;
}
struct Complex
{
    ll x,y;
    Complex(ll _x=0,ll _y=0) {
        x=_x%MOD,y=_y%MOD;
        if(x<0)x+=MOD;
        if(y<0)y+=MOD;
    }
    bool operator==(const Complex &q){return x==q.x&&y==q.y;}
    Complex operator+(const Complex &q){return {x+q.x,y+q.y};}
    Complex operator-(const Complex &q){return {x-q.x,y-q.y};}
    Complex operator*(const Complex &q){return {x*q.x+y*q.y*5,x*q.y+y*q.x};}
    Complex operator/(const Complex &d) {
        ll _d=(d.x*d.x-5*d.y*d.y)%MOD+MOD;
        ll _x=(x*d.x-5*y*d.y)%MOD+MOD;
        ll _y=(y*d.x-x*d.y)%MOD+MOD;
        return {_x*Inv(_d),_y*Inv(_d)};
    }
};
Complex POW(Complex _x, ll n) {
    Complex ret=1;
    for(;n;n>>=1){
        if(n&1)ret=ret*_x;
        _x=_x*_x;
    }return ret;
}
ll fac[MAX],inv[MAX];
ll C(ll n,ll m){return fac[n]*inv[n-m]%MOD*inv[m]%MOD;}
int solve()
{
    ll n,k;
    cin>>n>>k;
    fac[0]=inv[0]=inv[1]=1;
    for(int i=1;i<=k;i++)fac[i]=fac[i-1]*i%MOD;
    for(int i=2;i<=k;i++)inv[i]=inv[MOD%i]*(MOD-MOD/i)%MOD;
    for(int i=1;i<=k;i++)inv[i]=inv[i]*inv[i-1]%MOD;

    Complex A=Complex(1,1)/2;
    Complex B=Complex(1,-1)/2;
    Complex ans=0;
    for(ll i=0;i<=k;i++)
    {
        Complex q=POW(A,k-i)*POW(B,i);
        Complex a1=q;
        Complex s=0;
        if(q==1)s=a1*(n%MOD);
        else s=a1*(POW(q,n)-1)/(q-1);
        if(i%2)ans=ans-s*C(k,i);
        else ans=ans+s*C(k,i);
    }
    ans=ans/POW(Complex(0,1),k);
    cout<<ans.x<<endl;
    return 0;
}
int main()
{
    int T=1;
    //cin>>T;
    while(T--)solve();
    return 0;
}

G. Guessing Two Steps into the Multiverse

思路:用 i n [ i ] in[i] in[i] o u t [ i ] out[i] out[i]分别记录点 i i i的入度和出度。
每新增一条边 u , v u,v u,v,则总路径数增加 i n [ u ] + o u t [ v ] + [ u = = v ] in[u]+out[v]+[u==v] in[u]+out[v]+[u==v]
每一分钟可能增加的最大路径数为 i n i + o u t i + 1 in_i+out_i+1 ini+outi+1(增加边 i , i i,i i,i)或 max ⁡ ( i n i ) + max ⁡ ( o u t j ) \max(in_i)+\max(out_j) max(ini)+max(outj)(增加边 i , j i,j i,j)。
复杂度 O ( t ) O(t) O(t)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e5+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
ll in[MAX],out[MAX];
int solve()
{
    memset(in,0,sizeof in);
    memset(out,0,sizeof out);
    ll maxout=0,maxin=0,ans=0,em=0;
    int n,q;
    cin>>n>>q;
    while(q--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        ans+=in[x]+out[y]+(x==y);
        printf("%lld ",ans);
        out[x]++;
        in[y]++;
        maxout=max(maxout,out[x]);
        maxin=max(maxin,in[y]);
        em=max(em,in[x]+out[x]+1);
        em=max(em,in[y]+out[y]+1);
        printf("%lld\n",max(maxin+maxout,em));
    }
    return 0;
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

H. How Many Groups

思路:统计点 k k k到根路径上出现的不同数的个数, d f s dfs dfs即可。
复杂度 O ( n ) O(n) O(n)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())-1
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
vector<int>e[MAX];
int g[MAX],c[MAX],ans[MAX];
void dfs(int k,int a)
{
    c[g[k]]++;
    a+=(c[g[k]]==1);
    ans[k]=a;
    for(int i:e[k])dfs(i,a);
    c[g[k]]--;
}
int solve()
{
    memset(c,0,sizeof c);
    int n,root;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x)e[x].push_back(i);
        else root=i;
    }
    for(int i=1;i<=n;i++)scanf("%d",g+i);
    dfs(root,0);
    for(int i=1;i<=n;i++)printf("%d ",ans[i]);
    return 0;
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

I. Iron Fist Ketil vs King Canute

思路:比下大小即可。

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e3+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
int solve()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    return puts(n/k>=m?"Iron fist Ketil":"King Canute");
}
int main()
{
    int T=1;
//    cin>>T;
    while(T--)solve();
    return 0;
}

J. JP’s List of Trips

思路:如果2点间存在一条经过环的路径或者这2点本身就在环上,则这2点间的路径就不唯一,因为经过环则至少有2条路径。
那么接下来只要用拓扑排序删除图中的环,得到一个个联通块,然后再 d f s dfs dfs将联通块里的点用并查集合并即可。
复杂度 O ( n ) O(n) O(n)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e5+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
vector<pii>e[MAX];
int d[MAX],p[MAX],v[MAX];
int f(int x){return x==p[x]?x:p[x]=f(p[x]);}
void dfs(int k)
{
    if(d[k]>=2)return;
    for(auto &kv:e[k])
    {
        if(d[kv.fi]>=2||f(k)==f(kv.fi))continue;
        p[f(k)]=f(kv.fi);
        dfs(kv.fi);
    }
}
int solve()
{
    memset(d,0,sizeof d);
    memset(v,0,sizeof v);
    int n,m,t;
    cin>>n>>m>>t;
    for(int i=1;i<=n;i++)p[i]=i;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        e[x].push_back({y,i});
        e[y].push_back({x,i});
        d[x]++;
        d[y]++;
    }
    queue<int>q;
    for(int i=1;i<=n;i++)if(d[i]==1)q.push(i);
    while(!q.empty())
    {
        int x=q.front();q.pop();
        for(auto &kv:e[x])
        {
            if(v[kv.se])continue;
            v[kv.se]=1;
            d[kv.fi]--;
            if(d[kv.fi]<=1)q.push(kv.fi);
        }
    }
    for(int i=1;i<=n;i++)dfs(i);
    while(t--)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        puts(f(x)==f(y)?"YES":"NO");
    }
    return 0;
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

K. Knockout Spell

思路:按题意循环统计即可。复杂度 O ( n 2 ) O(n^2) O(n2)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e3+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
int a[MAX][MAX];
int solve()
{
    int n,k,ans=0;
    cin>>n>>k;
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)scanf("%d",&a[i][j]);
    for(int i=1;i+k-1<=n;i++)
    for(int j=1;j+k-1<=n;j++)
    {
        ans+=(a[i][j]==a[i+k-1][j]&&a[i][j]==a[i][j+k-1]&&a[i][j]==a[i+k-1][j+k-1]);
    }
    return printf("%d\n",ans);
}
int main()
{
    int T=1;
//    cin>>T;
    while(T--)solve();
    return 0;
}

L. ICPC Teams

思路:考虑二分答案 t ( t ≤ 500 ) t(t\le500) t(t500),那么 A , B , C 三人拥有的做题时间分别为 A ∗ t , B ∗ t , C ∗ t A,B,C三人拥有的做题时间分别为A*t,B*t,C*t A,B,C三人拥有的做题时间分别为At,Bt,Ct,然后用背包算法将 n n n个题目依次配给三人,若题目能分配完则满足条件。
复杂度 O ( n ∗ 5000 ∗ log ⁡ 2 500 ) O(n*5000*\log_2500) O(n5000log2500)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())-1
#define pii pair<int,int>
#define bit bitset<100000>
using namespace std;
const int MAX=1e5+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
int a,b,c,t[51];
int d[51][5001];
int f(int n,int x)
{
    vector<int>v;
    v.push_back(0);
    for(int i=1;i<=n;i++)v.push_back(t[i]);
    auto cal=[&](int speed) {
        memset(d,0,sizeof d);
        d[0][0]=1;
        for(int i=1;i<=sz(v);i++)
        for(int j=x*speed;j>=0;j--)
        {
            if(j>=v[i])d[i][j]|=d[i-1][j-v[i]];
            d[i][j]|=d[i-1][j];
        }
        int ma=-1;
        for(int i=0;i<=x*speed;i++)if(d[sz(v)][i])ma=i;
        for(int i=sz(v);i>=1;i--)
        {
            if(ma>=v[i]&&d[i][ma]&&d[i-1][ma-v[i]])
            {
                ma-=v[i];
                v.erase(v.begin()+i);
            }
        }
    };
    cal(c);
    cal(b);
    cal(a);
    return sz(v)<=0;
}
int solve()
{
    int n;
    cin>>n>>a>>b>>c;
    for(int i=1;i<=n;i++)scanf("%d",t+i);
    int l=1,r=500,ans=0;
    while(r>=l)
    {
        if(f(n,mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans<<endl;
    return 0;
}
int main()
{
    int T=1;
    while(T--)solve();
    return 0;
}

M. Modify the Array

思路: d [ i ] d[i] d[i]表示在执行了若干操作后以 a i a_i ai结尾的不同数组的数量,则 d [ j ] + = d [ i ] ( i < j 且 a j = min ⁡ ( a k ) i < k ≤ j ) d[j]+=d[i](id[j]+=d[i](i<jaj=min(ak)i<kj)复杂度 O ( n 2 ) O(n^2) O(n2)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=5e3+10;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
int a[MAX],d[MAX];
int solve()
{
    int n;
    cin>>n;
    memset(d,0,sizeof d);
    d[0]=1,a[0]=n+1;
    for(int i=1;i<=n;i++)scanf("%d",a+i);
    for(int i=1;i<=n;i++)
    for(int j=i-1,x=n+1;j>=0;j--)
    {
        if(min(a[j],a[i])<x)(d[i]+=d[j])%=MOD;
        x=min(x,a[j]);
    }
    int x=n+1,ans=0;
    for(int i=n;i>=1;i--)
    {
        if(x>a[i])(ans+=d[i])%=MOD;
        x=min(x,a[i]);
    }
    cout<<ans<<endl;
    return 0;
}
int main()
{
    int T=1;
    //cin>>T;
    while(T--)solve();
    return 0;
}

N. Necklace

思路:用 d [ i ] [ k ] d[i][k] d[i][k]表示采集 [ i , i + k − 1 ] [i,i+k-1] [i,i+k1]区间内珍珠可以获得的最大点数,当 d [ i ] [ k ] ≥ 0 d[i][k]\ge 0 d[i][k]0时,则可以更新答案。转移如下:
d [ i ] [ k ] = max ⁡ ( d [ i ] [ k − 1 ] + a i + k − 1 , d [ i + 1 ] [ k − 1 ] + a k ) d[i][k]=\max(d[i][k-1]+a_{i+k-1},d[i+1][k-1]+a_k) d[i][k]=max(d[i][k1]+ai+k1,d[i+1][k1]+ak)复杂度 O ( n ∗ k ) O(n*k) O(nk)

#include
#define fi first
#define se second
#define lson (k<<1)
#define rson (k<<1)+1
#define mid ((l+r)/2)
#define sz(x) int(x.size())
#define pii pair<ll,ll>
#define bit bitset<100000>
using namespace std;
const int MAX=1e5+101;
const int MOD=998244353;
const double PI=acos(-1.0);
typedef long long ll;
ll a[MAX],d[MAX][101];
int solve()
{
    int n,k;
    cin>>n>>k;
    k=min(k,n);
    for(int i=1;i<=n;i++)scanf("%lld",a+i);
    memset(d,-1,sizeof d);
    for(int i=1,x;i<=n;i++)
    {
        scanf("%d",&x);
        a[i]=(x==1?a[i]:-a[i]);
        d[i][1]=a[i];
    }
    for(int i=n+1;i<=n+k-1;i++)
    {
        a[i]=a[i-n];
        d[i][1]=d[i-n][1];
    }
    ll ans=0;
    for(int i=2;i<=k;i++)
    {
        ll sum=0;
        for(int j=1;j<=i;j++)sum+=max(0ll,-a[j]);
        for(int j=1;j<=n;j++)
        {
            if(d[j][i-1]>=0)d[j][i]=max(d[j][i],d[j][i-1]+a[j+i-1]);
            if(d[j+1][i-1]>=0)d[j][i]=max(d[j][i],d[j+1][i-1]+a[j]);
            if(d[j][i]>=0)ans=max(ans,sum);
            sum+=max(0ll,-a[j+i]);
            sum-=max(0ll,-a[j]);
        }
        for(int j=n+1;j<=n+k-1;j++)d[j][i]=d[j-n][i];
    }
    cout<<ans<<endl;
    return 0;
}
int main()
{
    int T=1;
    //cin>>T;
    while(T--)solve();
    return 0;
}

你可能感兴趣的:(ACM-ICPC,算法,ACM,ICPC)