2020 CCPC Wannafly Winter Camp Day6 (部分题解)

感谢敦哥敦哥友好签到题大放送,A题数目最多的一场

7-1 6A. Convolution

i = 1 n j = 1 n 2 a i a j \sum_{i=1}^n\sum_{j=1}^n2^{a_ia_j} 这个式子看上去很像卷积,如果是卷积的话直接NTT就做完了,所以我们想办法康康能不能化成卷积形式:
2 a i a j = 2 ( a i + a j ) 2 a i 2 a j 2 = 2 a i 2 2 a j 2 2 ( a i + a j ) 2 2^{a_ia_j}=\sqrt2^{(a_i+a_j)^2-a_i^2-a_j^2}=\sqrt2^{-a_i^2}*\sqrt2^{-a_j^2}*\sqrt2^{(a_i+a_j)^2}
这样把就把 a i a_i 对应的 2 a i 2 \sqrt2^{-a_i^2} a j a_j 对应的 2 a j 2 \sqrt2^{-a_j^2} 这两个的乘积与 ( a i + a j ) (a_i+a_j) 对应的 2 ( a i + a j ) 2 \sqrt2^{(a_i+a_j)^2} 对应起来,相加的值相同的归为一类,这样就转换成卷积的形式了
代码如下:

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
using namespace std;
#define LL long long int
using namespace std;
const ll hi = 116195171;
const int maxn = 1<<20,maxm = 100005,INF = 1000000000;
const int G = 3,P = (119 << 23) + 1;
const ll mod = (119<<23)+1;
int n,m,L,R[maxn];
int A[maxn],B[maxn];
ll qm(ll a, ll b){ll res = 1; while(b){if(b&1) res = res*a%mod; a = a*a%mod; b >>= 1; } return res; }
ll qpow(ll a,ll b){
    ll ans = 1;
    for (; b; b >>= 1,a = 1ll * a * a % P)
        if (b & 1) ans = 1ll * ans * a % P;
    return ans;
}
void NTT(int* a,int f){
    for (int i = 0; i < n; i++) if (i < R[i]) swap(a[i],a[R[i]]);
    for (int i = 1; i < n; i <<= 1){
        int gn = qpow(G,(P - 1) / (i << 1));
        for (int j = 0; j < n; j += (i << 1)){
            int g = 1;
            for (int k = 0; k < i; k++,g = 1ll * g * gn % P){
                int x = a[j + k],y = 1ll * g * a[j + k + i] % P;
                a[j + k] = (x + y) % P; a[j + k + i] = (x - y + P) % P;
            }
        }
    }
    if (f == 1) return;
    int nv = qpow(n,P - 2); reverse(a + 1,a + n);
    for (int i = 0; i < n; i++) a[i] = 1ll * a[i] * nv % P;
}
int a[maxm];
int main()
{
    scanf("%d", &n);
    ll invh = qpow(hi, mod-2);
    int len = 0;
    for(int i = 0; i < n; ++i) {
        scanf("%d", &a[i]); len = max(len , a[i]);
        ll t = qm(invh, (ll)a[i]*a[i]);
        A[a[i]] += t;
        A[a[i]] %= P;
    }

    len = 2*len+1; for (n = 1; n <= len; n <<= 1) L++;
    for (int i = 0; i < n; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) << (L - 1));

    NTT(A, 1);
    for (int i = 0; i < n; i++) A[i] = 1ll * A[i] * A[i] % P;
    NTT(A,-1);
    ll ans = 0;
    for(int i = 0; i < len; ++i){
        ans = (ans + A[i]*qm(hi, (ll)i*i))%mod;
    }
    cout<<ans<<endl;
}
/*
3
100000 5 1000
*/

7-3 6C. 酒馆战棋

模拟就完事了

#include<bits/stdc++.h>
using namespace std;

char s[1010];

int mxkill(int n, int a, int b, int c, int d){
    int res=0;
    for (int i=1;i<=n;++i){
        if (s[i]=='1'){
            if (c){
                c--;    res++;  continue;
            }
            if (d){
                d--;    c++;    continue;
            }
            if (a){
                a--;    res++;  continue;
            }
            if (b){
                b--;    a++;    continue;
            }
        }else{
            if (d){
                d--;    c++;    continue;
            }
            if (c){
                continue;
            }
            if (b){
                b--;    a++;    continue;
            }
        }
    }
    return res;
}

int mikill(int n, int a, int b, int c, int d){
    int res=0;
    for (int i=1;i<=n;++i){
        if (s[i]=='1'){
            if (d){
                d--;    c++;    continue;
            }
            if (c){
                c--;    res++;  continue;
            }
            if (b){
                b--;    a++;    continue;
            }
            if (a){
                a--;    res++;  continue;
            }
        }else{
            if (c){
                continue;
            }
            if (d){
                d--;    c++;    continue;
            }
            if (a){
                continue;
            }
            if (b){
                b--;    a++;    continue;
            }
        }
    }
    return res;
}

void sol(){
    int n;
    scanf("%d",&n);
    int a,b,c,d;
    scanf("%d%d%d%d",&a,&b,&c,&d);
    scanf("%s",s+1);
    printf("%d %d\n",mxkill(n,a,b,c,d),mikill(n,a,b,c,d));
    return ;
}

int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        sol();
    }
    return 0;
}

7-6 6F. 图与三角形

枚举每条边,这条边的两端点连的与该边不同色边数量/4(非整除)为对答案的贡献。

#include<bits/stdc++.h>
using namespace std;
long long A,B,C,P,D,ans;
vector<pair<pair<int,int>,int> > E;
long long n, sum;
long long du[2][5050];
void sol(){
    pair<int,int> edge;
    ans=0;
    int op,u,v;
    int lim=E.size();
    for (int i=0;i<lim;++i){
        edge=E[i].first;    op=E[i].second;
        u=edge.first;   v=edge.second;
        ans+=du[op^1][u];   ans+=du[op^1][v];
    }
    return ;
}


int main(){
    scanf("%lld",&n);
    sum = n*(n-1)*(n-2);    sum/=3;  sum/=2;
    scanf("%lld%lld%lld%lld%lld",&A,&B,&C,&P,&D);
    long long tmp;
    for (int i=1;i<=n;++i){
        for (int j=i+1;j<=n;++j){
            tmp=(A*(i+j)*(i+j)+B*(j-i)*(j-i)+C)%P;
            if (tmp>D){
                du[1][i]++; du[1][j]++;
                E.push_back(make_pair(make_pair(i,j),1));
            }else{
                du[0][i]++; du[0][j]++;
                E.push_back(make_pair(make_pair(i,j),0));
            }
        }
    }
    sol();
    cout<<sum-ans/4<<"\n";
    return 0;
}

7-7 6G. 单调栈

贪心,见代码(队友写的)

#include<bits/stdc++.h>
#define LL long long
#define inf 0x3f3f3f3f
#define test freopen("in","r",stdin);freopen("out","w",stdout);
#define lson rt<<1
#define rson rt<<1|1
#define PII pair<int,int>
using namespace std;
const int maxn=105;
int n,a[maxn],ans[maxn];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(ans,-1,sizeof(ans));
        scanf("%d",&n);
        for(int i=1;i<=n;++i) scanf("%d",a+i);
        int nc=0,p=1;
        while(nc!=n)
        {
            for(int i=n;i;--i)
            {
                if(a[i]==p) ans[i]=++nc;
            }
            for(int i=1;i<=n;++i)
            {
                if(a[i]==-1) {a[i]=p,ans[i]=++nc;break;}
                else if(a[i]==p) break;
            }
            ++p;
        }
        printf("%d",ans[1]);
        for(int i=2;i<=n;++i) printf(" %d",ans[i]);
        puts("");
    }
    return 0;
}

7-9 6I. 变大!

最后的结果肯定是把序列分成若干个值相等的块。那么我们可以考虑对于一个长度为n的块,它最后的成为值相等的块需要几步:
如果n是奇数,那么它的最大值两边数目要么都是奇数,要么都是偶数,如果都是偶数,那么除了第一次覆盖了3个位置,剩余每次覆盖两个位置,一共n/2次操作。如果都是奇数,那么以最大值所 位置为中心覆盖一次,然后两边剩余就都是偶数了。操作次数也是n/2.
n为偶数也一样分析出操作次数为n/2
所以用 d p ( i , j ) dp(i,j) 表示弄完前i个数字,操作了j次得到的最大值,然后枚举最后一个块的长度去转移就可,复杂度 O ( n 3 ) O(n^3)

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 55;
int n;
int a[maxn];
int dp[maxn][maxn];
int main()
{
    int T; cin>>T;
    while(T--){
        scanf("%d", &n);
        memset(dp, 0, sizeof dp);
        for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
        for(int i = 1; i <= n; ++i){
            int mx = a[i];
            for(int j = i-1; j >= 0; --j){
                int c = (i-j)/2;
                for(int k = 0; k +c <= i; ++k){
                    dp[i][k+c] = max(dp[i][k+c], dp[j][k] + mx*(i-j));
                }
                mx = max(mx, a[j]);
            }
        }
        for(int k = 1; k <= n; ++k){
            printf("%d", dp[n][k]);
            if(k != n) printf(" ");
            else printf("\n");
        }
    }
}

7-10 6J. K重排列

当k等于1的时候,答案为N!
否则因为每个点都可以回来,所以一定构成若干个环,并且这些环的长度都是k的约数。那么考虑dp加环
d p ( i , j ) dp(i,j) 表示考虑到第i个约数,加入了j个点的方案数,然后转移。
现在要计算从n个点里面选出t个k个点构成t个环的方案数。首先,n个点选t个k个点的无序集合的方案数为 F ( n , k , t ) = i = 0 t C ( n i k , k ) t ! = n ! t ! ( k ! ) t ( n t k ) ! F(n,k,t)=\frac{\sum_{i=0}^tC(n-ik,k)}{t!}=\frac{n!}{t!*(k!)^t*(n-tk)!}
每个 k k 点集合可以构成 ( k 1 ) ! (k-1)! 个不同圆环。所以再乘上 ( k 1 ) t (k-1)^t 就是从n个点里面选出t个k个点构成t个环的方案数。
然后根据这个dp就可以了

#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
const int maxn = 55;
const ll mod = 998244353;
ll fac[maxn], ifac[maxn], inv[maxn];
ll qm(ll a, ll b){
    ll res = 1;
    while(b){
        if(b&1) res = res*a%mod;
        a = a*a%mod;
        b >>= 1;
    }return res;
}
ll dp[maxn][maxn];
ll cal(int n, int k, int t){
    if(k == 0) return 1;
    ll res = fac[n]*ifac[t]%mod*qm(inv[k], t)%mod*ifac[n-t*k]%mod;
    return res;
}
int main()
{
    int n; ll k;
    fac[0] = ifac[0] = 1;
    for(int i = 1; i < maxn; ++i){
        fac[i] = fac[i-1]*i%mod;
        ifac[i] = qm(fac[i], mod-2);
        inv[i] = qm(i, mod-2);
    }
    int T; cin>>T;
    while(T--){
        scanf("%d%lld", &n, &k);
        if(n == 1){
            printf("1\n");
        }else if(k == 1){
            printf("%lld\n", fac[n]);
        }else{
            //k--;
            memset(dp, 0, sizeof dp);
            dp[0][0] = 1;
            int cur = 0;
            for(int i = 1; i <= n; ++i){
                if(k%i) continue;
                for(int j = 0; j <= n; ++j){
                    if(!dp[cur][j]) continue;
                   // cout<<"i:"<<i<<" j:"<<j<<" "<<dp[cur][j]<<endl;
                    for(int u = 0; j + u <= n; u += i){
                        //cout<<"u/i:"<<u/i<<endl;
                        dp[cur+1][j+u] += dp[cur][j]*cal(n-j,i, u/i)%mod;
                        dp[cur+1][j+u] %= mod;
                       // cout<<"res:"<<dp[cur][j]*cal(n-j,i, u/i)<<endl;
                    }
                }
                cur++;
            }
            printf("%lld\n", dp[cur][n]);
        }
    }
}

4个快乐签到题就不赘述了~

发布了102 篇原创文章 · 获赞 30 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43202683/article/details/104025352