2018.07.13【2018提高组】模拟C组

前言:终于乐观(optimistic)(打表打漏了,还是比较高)


JZOJ 3382 七夕祭

题目

环(jiang)形(de)均(tai)分(cao)纸(shuai)牌(le)


分析

首先在做这道题之前,要知道均分纸牌,设 A [ i ] = C [ i ] S U M / N ,然后S[i]是A[i]的前缀和,答案就是 | S [ i ] | ,但是这是环形的,当从k断开可以得到最优解,那么

A S
K+1 A[K+1] S[K+1]-S[K]
K+2 A[K+2] S[K+2]-S[K]
N A[N] S[N]-S[K]
1 A[1] S[1]+S[N]-S[K]
2 A[2] S[2]+S[N]-S[K]
K A[K] S[K]+S[N]-S[K]

因为S[N]=0,所以可以转换成最小的 i = 1 n | S [ i ] S [ k ] | ,所以就想到了中位数,不需要枚举k,中位数就是k,then时间复杂度O(nlogn+mlogm)


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
int a[100001],b[100001],n,m,t;
unsigned int ans1,ans2;
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int abs(int a){return (a<0)?-a:a;}
int main(){
    n=in(); m=in(); t=in();
    for (int i=1;i<=t;i++) a[in()]++,b[in()]++;
    for (int i=1;i<=n;i++) a[i]+=a[i-1]-t/n;
    for (int i=1;i<=m;i++) b[i]+=b[i-1]-t/m;
    if (t%n&&t%m) return !printf("impossible");
    stable_sort(a+1,a+1+n); stable_sort(b+1,b+1+m);
    for (int i=1;i<=n;i++) ans1+=1ll*abs(a[i]-a[(n+1)>>1]);
    for (int i=1;i<=m;i++) ans2+=1ll*abs(b[i]-b[(m+1)>>1]);
    if (!(t%n)&&!(t%m)) return !printf("both %lld",ans1+ans2);
    else if (t%m) return !printf("row %lld",ans1); else return !printf("column %lld",ans2); 
}

JZOJ 3383 太鼓达人

题目

长度为M的环形01序列。从不同的位置出发可以得到M个长度为K的01串。M个01串应该是互不相同的。给出K,求出M的最大值,并给出字典序最小的方案。


分析

首先可以发现最后会跳回原来的串,所以就想到了欧拉图,所以深搜。很显然发现答案就是x
当前为x,向 2 x 2 k 1 2 x + 1 2 k 1 ,答案就是x二进制下的第一位,时间复杂度O( 2 k


代码

#include <cstdio>
using namespace std;
int n,pow; bool ans[2051],v[2051];
bool dfs(int x,int k){
    if (v[x]) return 0;
    if (k==pow) return 1;
    v[x]=1; ans[k]=x&1;
    if (dfs((x<<1)&(pow-1),k+1)) return 1;//顺序很重要
    if (dfs((x<<1|1)&(pow-1),k+1)) return 1;
    return v[x]=0; 
}
int main(){
    scanf("%d",&n); printf("%d ",pow=1<<n);
    for (int i=1;i<n;i++) putchar('0'); dfs(0,1);
    for (int i=1;i<=pow-n+1;i++) putchar(ans[i]+48); 
    return 0;
}

JZOJ 3384 理科男

题目

求在k进制下,a/b小数部分的非循环节长度(即有限小数和混循环小数的前部分)和循环节长度


分析

首先要介绍正向取整法,
0.3 × 3 = 0.9 , 0.9 × 3 = 2.7 , 0.7 × 3 = 2.1 , 0.1 × 3 = 0.3
所以 ( 0.3 ) 10 = ( 0.022 ) 3 ( 022 )
所以50分的代码就是用map库或者哈希存下余数,如果余数出现过那么退出,但是非常慢。
2333333333333333333333333333333333333333333333333333333333333333333333333
考虑 ( 7 30 ) 10 = ( 7 15 ) 10 ÷ 2 = ( 7 3 ) 10 ÷ 10
所以处理混循环小数和有限小数,保证gcd(a,b)=1,之后
w h i l e ( ( d = g c d ( b , c ) ) > 1 ) i n c ( a n s ) , b ÷ = d ,ans就是第一个问题的答案。
当b=1时证明是有限小数,否则处理过后的混循环小数变成纯循环小数。
思考 a k q a ( m o d p ) ,q即所求答案,两边同时除以a得到 k q 1 ( m o d p )
根据欧拉定理, a φ ( p ) 1 ( m o d p ) ,但 φ ( p ) 不是最优解,所以在 φ ( p ) 的最小因数中找到答案。
why?the reason is kind of easy.
反证法,假设 a x 1 ( m o d p ) 的最小整数解 x 0 不是 φ ( p ) 的因数。
φ ( p ) = n x 0 + r ( 0 < r < x 0 ) , 因为 a x 0 1 ( m o d p ) 所以 a n x 0 1 ( m o d p )
根据欧拉定理 a φ ( p ) 1 ( m o d p ) ,所以 a r 1 ( m o d p ) ,所以假设不成立。证毕。
时间复杂度 O ( b )


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
using namespace std;
typedef long long ll;
ll a,b,c,t,d;
ll in(){
    ll ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
void print(ll ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
ll mul(ll a,ll x){
    ll ans=0;
    while (x){
        if (x&1) ans=(ans+a)%b;
        a=(a<<1)%b; x>>=1;
    }
    return ans;
}
ll ksm(ll a,ll x){
    ll ans=1;
    while (x){
        if (x&1) ans=mul(ans,a);
        a=mul(a,a); x>>=1;
    }
    return ans;
}
ll answer(){
    ll n=b,x=b,k=0;
    for (int i=2;(ll)i*i<=n;i++)
    if (n%i==0){
        x=x/i*(i-1);
        while (n%i==0) n/=i;
    }
    if (n>1) x=x/n*(n-1); n=x;
    for (int i=2;(ll)i*i<=x;i++)
    if (x%i==0){
        while (n%i==0&&ksm(c,n/i)==1) n/=i; 
        while (x%i==0) x/=i;
    }
    if (x>1&&ksm(c,n/x)==1) n/=x;
    return n;
}
int main(){
    t=in();
    while (t--){
        a=in(); b=in(); c=in(); ll ans=0;
        a/=(d=__gcd(a,b)); b/=d;
        while ((d=__gcd(b,c))>1) ans++,b/=d;
        if (ans) print(ans); else putchar('0'); putchar(' '); ans=0;
        if (b>1) ans=answer();
        if (ans) print(ans); else putchar('0'); putchar('\n');
    }
    return 0;
}

JZOJ 3385 黑魔法师之门

题目

2 1


分析

用并查集,当前连的边不需要连就可以连通那么ans=ans*2(初始值为1),否则合并,每次连边后输出ans-1,按秩合并+路径压缩后时间复杂度 O m   α ( N ) ( 6 m )


代码

#include <cstdio>
#include <cctype>
#define mod 1000000009
using namespace std;
int n,m,ans=1,x,y,f[200001];
int in(){
    int ans=0; char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=ans*10+c-48,c=getchar();
    return ans;
}
int min(int a,int b){return (a<b)?a:b;}
int max(int a,int b){return (a>b)?a:b;}
void print(int ans){if (ans>9) print(ans/10); putchar(ans%10+48);}
int getf(int u){return (f[u]==u)?u:f[u]=getf(f[u]);}
int main(){
    n=in(); m=in();
    for (int i=1;i<=n;i++) f[i]=i;
    while (m--){
        x=in(); y=in();
        int u=getf(x); int v=getf(y);
        if (u==v) ans=(1ll*ans*2)%mod;
        else f[min(u,v)]=max(u,v);
        if (ans>1) print(ans-1); 
        else putchar('0'); putchar('\n');
    }
    return 0;
}

后续

codevs 2485 七夕祭 洛谷 2512 JZOJ 1472 糖果传递 洛谷 1031 均分纸牌 codevs 2486 太鼓达人
codevs 2487 理科男 codevs 1995 黑魔法师之门

猜你喜欢

转载自blog.csdn.net/sugar_free_mint/article/details/81038676