HGOI20181029模拟题解

HGOI20181029模拟题解

 

/*
sxn让我一定要谴责一下出题人和他的数据!
*/

 

problem:

给出十进制数a,b,然后令(R)10=(a)10*(b)10,给出c表示一个k进制数(1<k<=16)问(R)k=(c)k在k等于多少时成立,求k的最小值

如果无解输出0.

sol:显然a,b比较大的时候a*b一定爆longlong,考虑一个问题,显然若a*b>1e18那么c一定不可能等于R,一定小于等于R(主要是由于给出的c不含有字母)

然后弄个stack来求一下a*b转k进制,暴力枚举k即可,复杂度O(k*T*w)其中w是常数w=位数约等于18

code:

# include <bits/stdc++.h>
#ifdef LOCAL-ljc 
#pragma GCC optimze(2)
#endif
# define int long long
using namespace std;
const int MAXN=75;
char s[MAXN];
inline int read()
{
    int X=0,w=0; char c=0;
    while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
    while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
    return w?-X:X; 
}
inline void print(int x)
{
    if (x<0){ putchar('-');x=-x;}
    if (x>9) print(x/10);
    putchar('0'+x%10);
}
char val(int x)
{
    if (x>=0&&x<=9) return x+'0';
    else return x-10+'A';
}
bool cmp(char *s1,char *s2,int len)
{
    for (int i=0;i<len;i++)
     if (s1[i]!=s2[i]) return false;
    return true;
}
bool check(int x,int Base)
{ 
    char E[MAXN]; stack<int>st; 
    int rec=x;
    while (x) { st.push(x%Base); x/=Base;}
    int len=0;
    while(!st.empty()) { E[len++]=val(st.top());st.pop();}
    if (cmp(E,s,len)) return 1;
    else return 0; 
}
signed main()
{
    #ifdef LOCAL-ljc
        freopen("input.in","r",stdin);
        freopen("output.out","w",stdout);
    #else 
        freopen("base.in","r",stdin);
        freopen("base.out","w",stdout);
    #endif 
    int T=read();
    while (T--) {
        int p=read(),q=read(); int R=p*q;
        if ( p> (int) (1e18) / q) {
            print(0),putchar('\n');
            continue;
        }
        cin>>s;
        bool f=false; 
        for (int i=2;i<=16;i++) 
            if (check(R,i)) { print(i); putchar('\n'); f=true; break;}  
        if (!f) { putchar('0');putchar('\n');}    
    }
    return 0;
}

 

problem:给出置换关系a[]*p[]=c[] ,其中c_(p_i)=a_i,问一个有序排列[1,2,3,....n]通过最少k(k>0)次和P数组置换可以重新变成有序排列[1,2,3...n]

sol:发现置换,然后想到置换环,然后经过一次置换以后所有置换环,均向同一方向旋转一次,然后问你多少次置换重新变成有序,就是求置换环大小的lcm

按照样例来说:

5
3 4 1 5 2

其中:置换环有2个 分别是[1,3] [2,4,5]这里[]中的数组下标,然后可以发现长度是(2,3),答案就是lcm(2,3)=6

我们可以O(n)找出所有置换环,问题是求出lcm(o1,o2...ok)

显然如果输入比较大的时候lcm容易爆longlong,然后我们可以gaojing大法?

不行,不能打高精度!(看到模数)

lcm就是最小公倍数,那么对每一个数进行质因数分解

ai=Piki   然后lcm{ai}=Pimax(ki)

打质因数分解23333

我怕爆掉然后就打了两个算法。。

# include <bits/stdc++.h>
#ifdef LOCAL-ljc
#pragma GCC optimze(2)
#endif
# define int long long
using namespace std;
const int MAXN=1e5+10;
const int mo=19184192;
bool vis[MAXN];
int n,p[MAXN],ans,o[MAXN];
int max_Num;
inline int read()
{
    int X=0,w=0; char c=0;
    while (!(c>='0'&&c<='9')) w|=c=='-',c=getchar();
    while (c>='0'&&c<='9') X=(X<<1)+(X<<3)+(c^48),c=getchar();
    return w?-X:X; 
}
inline void print(int x)
{
    if (x<0){ putchar('-');x=-x;}
    if (x>9) print(x/10);
    putchar('0'+x%10);
}
int gcd(int a,int b)
{
    if (b==0) return a;
    else return gcd(b,a%b);
} 
int lcm(int a,int b){return a*b/gcd(a,b);}

void dfs(int u)
{
    ans++; vis[u]=true;
    if (vis[p[u]]==true) return;
    else dfs(p[u]);
}

vector<int>P;
bool prime[MAXN];
int a[MAXN];

void getprime(int MAXN)
{
    memset(prime,true,sizeof(prime));
    P.clear(); prime[0]=prime[1]=false;
    for (int i=2;i<=MAXN;i++) {
        if (!prime[i]) continue; P.push_back(i);
        if (i+i>MAXN) continue;
        for (int j=i+i;j<=MAXN;j+=i) prime[j]=false;
    } 
}
void in(int num)
{
    for (int i=0;i<P.size();i++) {
        int tmp=0; 
        while (num%P[i]==0) tmp++,num/=P[i];
        a[P[i]]=max(a[P[i]],tmp);    
    } 
     
}
int pow(int x,int n,int mo)
{
    if (n==0) return 1;
    int t=pow(x,n/2,mo)%mo;
    t=t*t%mo;
    if (n%2==1) t=t*x%mo;
    return t%mo;
}
int getlcm()
{
    memset(a,0,sizeof(a));
    for (int i=1;i<=o[0];i++) {
        int num=o[i]; in(num);
    }
    int ret=1;
    for (int i=0;i<P.size();i++)
     if (a[P[i]]!=0) ret=ret*pow(P[i],a[P[i]],mo)%mo;
    return ret; 
}
signed main()
{
    #ifdef LOCAL-ljc
        freopen("perm.in","r",stdin);
    #else
        freopen("perm.in","r",stdin);
        freopen("perm.out","w",stdout);
    #endif
    n=read();
    for (int i=1;i<=n;i++) p[i]=read();
    memset(vis,false,sizeof(vis));
    for (int i=1;i<=n;i++) {
        if (vis[i]) continue;
        ans=0;
        if (vis[i]==false) dfs(i);
        o[++o[0]]=ans; 
    }
    bool flag=true;
    ans=1;
    for (int i=1;i<=o[0];i++) {
        ans=lcm(ans,o[i]);
        if (ans<0) flag=false;
    }
    if (flag) { print(ans%mo);putchar('\n'); return 0;}
    ans=1;
    max_Num=0;
    for (int i=1;i<=o[0];i++) max_Num=max(max_Num,o[i]);
    getprime(max_Num);
    print(getlcm()); 
    putchar('\n');
    return 0;
}

problom:算n个数都是n的二十四点

sol:打表+找规律,

若n>=12,有这样的规律,

如果n是奇数,那么可以分解为(3n)/n * (8n)/n +n-n+n-n...显然前面用掉的是(3+1+8+1=13)后面的+n-n...一定是偶数,所以可以消为0)

如果n是偶数,那么可以分解成(4n/n)*(6n)/n+n-n+n-n...显然前面用掉的是(4+1+6+1=12)后面+n-n...一定是偶数,所以可以消掉为0)

否则n<=12暴力打表算。

code:

 

# include <bits/stdc++.h> 
using namespace std;  
int main()  
{  
//    freopen("card.in","r",stdin);
//    freopen("card.out","w",stdout);
    int n,tmp, T; 
    scanf ("%d", &T);
    for (int t = 1; t <= T; ++ t){  
        scanf ("%d", &n);
        switch (n) {
            case 1:printf("-1\n");break;
            case 2:printf("-1\n");break;
            case 3:printf("-1\n");break;
            case 4:printf("1 * 2\n5 + 3\n6 + 4\n");  break;
            case 5:printf("1 * 2\n3 / 6\n4 - 7\n5 * 8\n"); break;
            case 6:printf("1 + 2\n3 + 4\n5 - 6\n7 + 8\n10 - 9\n");  break;
            case 7:printf("1 + 2\n3 + 8\n9 / 4\n10 + 5\n11 + 6\n12 + 7\n"); break;
            case 8:printf("1 + 2\n3 + 9\n4 - 5\n11 * 6\n12 * 7\n13 * 8\n10 + 14\n");break;
            case 9:printf("1 + 2\n3 + 10\n4 / 5\n6 / 7\n8 / 9\n11 - 12\n15 - 13\n 16 - 14\n");  break;
            case 10:printf("1 + 2\n3 / 4\n5 / 6\n7 / 8\n9 / 10\n11 + 12\n16 + 13\n17 + 14\n18 + 15\n");break;
            case 11:printf("1 + 2\n3 / 4\n5 / 6\n7 - 8\n15 * 9\n16 * 10\n17 * 11\n12 + 13\n19 + 14\n20 + 18\n");  break;
            case 12:printf("1 + 2\n3 - 4\n5 * 14\n6 * 15\n7 * 16\n8 * 17\n9 * 18\n10 * 19\n11 * 20\n12 * 21\n13 + 22\n");  break;
            case 13:printf("1 + 2\n3 / 4\n5 / 6\n7 - 8\n17 * 9\n18 * 10\n19 * 11\n20 * 12\n21 * 13\n22 + 14\n23 - 15\n24 - 16\n"); break;
            default: {
                printf("1 + 2\n3 + 4\n5 + 6\n7 + 8\n9 + 10\n");  
                printf("%d + %d\n%d + %d\n%d + %d\n",n+1,n+2,n+3,n+4,n+5,n+6);  
                printf("%d / 11\n%d / 12\n",n+7,n+8);  
                printf("%d * %d\n",n+9,n+10);  
                printf("13 - 14\n");  
                tmp=n-14;  
                int i;  
                for(i=0;i<tmp;i++) printf("%d * %d\n",n+12+i,15+i);  
                printf("%d + %d\n",n+11,n+12+tmp);  
                }  
                break;
            }
    }  
    return 0;  
}  

 

转载于:https://www.cnblogs.com/ljc20020730/p/9870273.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值