zoukankan      html  css  js  c++  java
  • 8.29 正睿 NOIP 十连测 Day1 题解

    T1

    推式子:

    [sumlimits_{i=1}^nsumlimits_{j=1}^pphi(i^j)=sumlimits_{i=2}^nfrac{phi(i) imes (i^p-1)}{i-1}+p ]

    所以我们筛一下 (f(x)=phi(x) imes x^p)(phi(x)) 还有线性逆元就做完了。注意快速幂只对素数做就可以。所以复杂度正确。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 10000100
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    const int mod=1e9+7;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    int n,p;
    
    inline ll ksm(ll a,ll b,ll mod){
        ll res=1;while(b){if(b&1) res=res*a%mod;a=a*a%mod;b>>=1;}return res;
    }
    
    ll f[N],phi[N],Pow[N],inv[N],ans;
    int Prime[N],tail;
    bool NotPrime[N];
    
    inline void PreWork(int n){
        NotPrime[1]=1;f[1]=1;phi[1]=1;
        for(int i=2;i<=n;i++){
            if(!NotPrime[i]){Prime[++tail]=i;phi[i]=i-1;Pow[i]=ksm(i,p,mod);f[i]=phi[i]*Pow[i]%mod;}
            for(int j=1;j<=tail&&i*Prime[j]<=n;j++){
                int k=i*Prime[j];NotPrime[k]=1;
                if(i%Prime[j]==0){
                    phi[k]=phi[i]*Prime[j]%mod;f[k]=f[i]*Prime[j]%mod*Pow[Prime[j]]%mod;break;
                }
                else{
                    phi[k]=phi[i]*phi[Prime[j]]%mod;f[k]=f[i]*f[Prime[j]]%mod;
                }
            }
        }
        // for(int i=1;i<=n;i++) printf("%lld ",f[i]);puts("");
        // for(int i=1;i<=n;i++) printf("%lld ",phi[i]);puts("");
        inv[1]=1;
        for(int i=2;i<=n;i++){inv[i]=(mod-mod/i)*inv[mod%i]%mod;}
        // for(int i=1;i<=n;i++) printf("%lld ",inv[i]);puts("");
    }
    
    int main(){
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
        read(n);read(p);PreWork(n);
        for(int i=2;i<=n;i++){
            ans=(ans+inv[i-1]*(f[i]-phi[i])%mod)%mod;
        }
        ans=(ans+p)%mod;
        printf("%lld
    ",(ans%mod+mod)%mod);
        return 0;
    }
    

    T2

    大根堆考虑从大到小依次考虑每一个数。设当前数为 (x),可以放置的位置个数为 (res),那么如果 (x) 没有钦定为叶子,放置完 (x) 后会 (res:=res+1),否则 (res:=res-1),随时维护 (res),然后乘起来就可以。

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 1000010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    const int mod=1e9+7;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    bool vis[N];
    int n,S,s[N];
    ll ans=1,res=1;
    
    int main(){
        read(n);read(S);
        for(int i=1;i<=S;i++){
            read(s[i]);vis[s[i]]=1;
        }
        if(n==1){puts("1");return 0;}
        if(vis[n]){puts("0");return 0;}
        for(int i=n;i>=1;i--){
            ans=ans*res%mod;
            if(vis[i]) res--;
            else res++;
        }
        printf("%lld
    ",ans);
        return 0;
    }
    

    T3

    用的 zyc 大佬的思路,这里说的详细一点。

    我们考虑这个序列字典序最大是多少,假设 (n=4),那么这个序列是:

    [ exttt{4,4,4,4,4,4,3,2,2,1,1,1} ]

    我们考虑这个序列是怎么形成的,一开始有 (n) 个空点,然后相邻连边,就可以使 scc 个数不变。加的不能加为止,不难发现,每多连一条边,就可以有 (x) 条边使得 scc 个数为 (n-x) 不变。这个自己画一下图就能论证。

    接下来我们考虑如果已知一个前缀 scc 序列,如何看下一个值最小能填多少?我们不难发现,这个数是 (n-k+1) 其中 (n) 是序列长度,(k) 是序列中不同的数的个数。这个很好论证,用构造的方法就可以证明。

    至于上界,这个就是上一位和这一位的 (down) 值做 (min) 就可以。

    上下界固定后,暴力就好想了,dp 也很自然。

    (f_{i,j,k}) 表示序列长度为 (i),最后一个数为 (j),有 (k) 个不同的数的方案数。

    我们考虑有哪些状态可以转移到 (f_{i,j,k})。我们考虑如果这个序列中 (j) 不止一个,那么就可以从 (f_{i-1,j,k}) 中转移。

    否则,我们枚举第 (i-1) 个填了什么,然后转移,不难发现这个东西是 (f_{i-1,j',k-1}) 其中 (j'>j),因为不合法的状态我们已经设置为 (0),所以可以放心转移。用前缀和优化,可以做到 (O(n^4))

    代码:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 101
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    template<typename T> inline T Max(T a,T b){return a<b?b:a;}
    
    int t;
    int f[N*N][N][N],sum[N][N],mod,ans[N*N],down[N*N],n;
    
    inline void GetDown(int n){
        for(int i=1;i<=n*(n-1)/2;i++) down[i]=n;
        int len=n*(n-1)/2;
        for(int i=1;i<=n-1;i++){
            for(int j=1;j<=i;j++) down[++len]=n-i;
        }
        // for(int i=1;i<=n*(n-1);i++) printf("%d ",down[i]);
    }
    
    inline int Del(int x){
        return x>=mod?x-mod:x;
    }
    
    inline void Clear(){
        memset(f,0,sizeof(f));memset(sum,0,sizeof(sum));memset(down,0,sizeof(down));
        memset(ans,0,sizeof(ans));
    }
    
    #define Sum(l,r) (sum[r][k-1]-sum[l-1][k-1])
    inline void Solve(int n){
        // f[i][j][k] 表示序列长度为i,序列最后一个数为j,前面一共有k个不同的数。
        f[1][n][1]=1;printf("1 ");
        for(int i=2;i<=n*(n-1);i++){
            for(int j=1;j<=n&&j<=i;j++){
                sum[1][j]=f[i-1][1][j];
                for(int k=2;k<=down[i-1];k++)
                    sum[k][j]=Del(sum[k-1][j]+f[i-1][k][j]);
            }//预处理前缀和,sum[k][j]=f[i-1][1...k][j]
            for(int j=1;j<=down[i];j++){
                for(int k=1;k<=n&&k<=i&&k<=(n-j+1);k++){
                    //枚举状态 f[i][j][k]
                    int now=Max(j,n-(i-k+1));
                    //计算下界
                    if(now>j) continue;
                    //如果比 j 大,那么状态不合法。
                    f[i][j][k]=(Sum(now+1,down[i-1])+mod)%mod;
                    f[i][j][k]=Del(f[i][j][k]+f[i-1][j][k]);
                    ans[i]=Del(ans[i]+f[i][j][k]);
                }
            }
            printf("%d ",ans[i]);
        }
        puts("");
    }
    
    signed main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        read(t);
        while(t--){
            Clear();
            read(n);read(mod);
            GetDown(n);Solve(n);
        }
    }
    

    T4

    首先,设 (x) 为所有数的异或值,如果 (x=0) ,那么就是平局,否则,找到最高位的 (1) ,这一位上 (1) 的个数为奇数,表示 Alice 和 Bob 一定不同,所以题目简化成给定长度为 (n)(01) 串,来做那些操作判断是否先手必胜。

    我们考虑如果 (n) 是偶数,那么先手必胜,理由是我们对整个串按照下标是否为奇数进行分组,我们发现因为 (n) 是偶数,所以先手可以选所有偶数位置或所有奇数位置,因为 (1) 的个数为奇数,所以一定有一种方案使得 (1) 的个数为奇数。

    如果 (n) 是奇数,我们考虑如果两端都是 (0),那么先手必败,这是因为无论先手选哪个,后手面对的都是上面的先手必胜态,所以先手必败。

    考虑两端至少有一个 (1)。可以肯定的是先手必须选 (1),剩下的我们来看怎么选先手能胜。不难发现,需要满足两个要求:

    • 首先,无论接下来后手选什么,先手都必须选和后手一样的。这是因为如果选的不一样,这就转化成了上面的必胜态,所以先手必输。
    • 并且,剩下的 (1) 的个数必须满足是 (4) 的倍数。否则,即使满足上面的要求,选到最后先手手中的数字会变成 (0)

    判断即可。

    需要注意的是,如果两端都是 (1) ,那么需要判断两段,数据在程序末尾:

    #include<bits/stdc++.h>
    #define dd double
    #define ld long double
    #define ll long long
    #define uint unsigned int
    #define ull unsigned long long
    #define N 100010
    #define M number
    using namespace std;
    
    const int INF=0x3f3f3f3f;
    
    template<typename T> inline void read(T &x) {
        x=0; int f=1;
        char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        x*=f;
    }
    
    int all,a[N],n,cnt,t;
    
    inline void Init(){
        read(n);cnt=0;all=0;
        for(int i=1;i<=n;i++){
            read(a[i]);all^=a[i];
        }
    }
    
    inline bool Check(ll l, ll r)
    {
    	while(l < r && a[l] == a[r]) l++,r--;
        for(int i=l;i<=r;i+=2){
    		if(a[i]!=a[i+1]) return 0;
    	}
    	return 1;
    }
    
    inline void Solve(){
        int w=-1;
        for(int i=30;i>=0;i--)
            if(((all>>i)&1)==1){w=i;break;}
        if(w==-1){puts("Draw");return;}
        for(int i=1;i<=n;i++){
            a[i]=(a[i]>>w)&1;if(a[i]) cnt++;
        }
        if((n&1)==0){puts("Alice");return;}
        if(a[1]==a[n]&&a[1]==0){puts("Bob");return;}
        cnt--;if(cnt%4!=0){puts("Bob");return;}
        if((a[n]&&Check(1,n-1))||(a[1]&&Check(2,n))){puts("Alice");return;}
        puts("Bob");
    }
    
    int main(){
        // freopen("my.in","r",stdin);
        // freopen("my.out","w",stdout);
        read(t);
        while(t--){
            Init();Solve();
        }
        return 0;
    }
    /*
    1
    19
    1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 
    */
    
  • 相关阅读:
    javaweb
    反射 day1
    JDBC-day1
    总结
    day5
    day4
    day3
    18.10.17 考试总结
    洛谷P2172 [bzoj] 2150 部落战争
    18.10.15 考试总结
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/15204350.html
Copyright © 2011-2022 走看看