Codeforces Round #305 (Div. 2)

C.已知两个数h1和h2,每次作如下变换:(x1*h1+y1)%m,(x2*h2+y2)%m,得到新数仍记为h1和h2。问最少经过多少步能够满足h1==a1&&h2==a2。
解法:寻找这种变换的最小正周期,缩小搜索范围即可。
当x1!=1时,可以得到这种变换的周期是phi(m)
当x1==1时,可以得到这种变换的周期是m。
1.当x1和x2同时为1或同时不为1时,在1-m中是否存在解等价于在整个实数域中是否存在解。
2.当x1==1&&x2!=1时,只要在1-m中先找到h1的变换可行解(h1==a1),然后对于每个1-m中的可行解,每次增加m,再找寻phi(m)次即可,总复杂度O(k*m)

#include <string>
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include<algorithm>
#define ll long long
using namespace std;

const ll maxn = 1000000+10;

ll m,ans;
ll x1,y1,h1,a1;
ll x2,y2,h2,a2;
ll tmp[maxn],cnt;
ll dp[maxn];

ll Power(ll a,ll b){
    ll ans=1;
    while(b){
        if(b&1) { ans=(ans*a)%m; b--; }
        a=(a*a)%m; b>>=1;
    }
    return (ans%m);
}

void gcd(ll a,ll b,ll &d,ll &x,ll &y){
    if(!b) { d=a; x=1; y=0; }
    else { gcd(b,a%b,d,y,x); y-=x*(a/b); }
}

ll ni(ll a){
    ll d,x,y;
    gcd(a,m,d,x,y);
    return d==1?(x+m)%m:-1;
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%I64d",&m);
    scanf("%I64d%I64d%I64d%I64d",&h1,&a1,&x1,&y1);
    scanf("%I64d%I64d%I64d%I64d",&h2,&a2,&x2,&y2);
    bool flag=false;
    ans=0;
    if(x1==1&&x2==1||(x1!=1&&x2!=1)){
        for(ll i=1;i<=m;i++){
            h1=(h1*x1+y1)%m,h2=(h2*x2+y2)%m;
            if(h1==a1&&h2==a2) { flag=true; ans=i; break; }
        }
    }
    else{
        if(x2==1) { swap(h1,h2); swap(a1,a2); swap(x1,x2); swap(y1,y2); }
        ll tmph1=h1;
        cnt=0;
        for(ll i=1;i<=m;i++){
            tmph1=(tmph1+y1)%m;
            if(tmph1==a1) { tmp[cnt++]=i; }
        }
        ll n=ni(x2-1);
        for(ll i=0;i<cnt;i++){
            dp[i]=Power(x2,tmp[i]);
        }
        ll xi=Power(x2,m);
        for(ll i=0;i<=m&&!flag;i++){
            for(ll j=0;j<cnt&&!flag;j++){
                ll id=i*m+tmp[j];
                ll tmph2=((dp[j]*h2)%m+((y2*(dp[j]-1))%m*n)%m)%m;
                if(tmph2==a2) {  flag=true; ans=id; break; }
                dp[j]=(dp[j]*xi)%m;
            }
        }
    }
    if(flag) { printf("%I64d\n",ans); }
    else printf("-1\n");
    return 0;
}

D.n个数组成的数列,定义连续区间中的最小值为该区间的权值,求长度为k的连续区间的最大权值。
解法:先将n个数从大到小排序。长度为1的区间最大权值一定是最大值。其次,考虑以第二大数为权值的区间最大长度(正难则反:原意是求区间长度为k的最大权值)。这个需要将数列扫描两遍,找到左边和右边离第二大数最近且第一个小于它的数字位置。这个一开始我想着可以用线段树做,后来参考别人做法,可以用栈来进行维护。用这种方法可以求得每个数的r[i]和l[i],于是得到下述结论。
结论:求每个数的l[i]和r[i],l[i]是指第i个数左侧第一个小于a[i]的数字下标,r[i]同理可以在O(n)内求得。
r[i]-l[i]-1即为所求:以a[i]为权值的区间最大长度
对于排序完成的序列来说,可以扫描一遍序列判断长度为k的序列能够以当前值作为最大权值。

#include <string>
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#define ll long long
using namespace std;

const int maxn = 200000+10;

int n;
int l[maxn],r[maxn],len[maxn];
struct Node{
    int a,id;
}st[maxn];
int cnt;

bool cmp(Node a,Node b){
    return a.a>b.a;
}

void init(){
    stack<int> s;
    for(int i=1;i<=n;i++){
        while(!s.empty()&&st[s.top()].a>=st[i].a) s.pop();
        if(s.empty()) l[i]=0;
        else l[i]=s.top();
        s.push(i);
        //printf("%d\n",i);
    }
    while(!s.empty()) s.pop();
    for(int i=n;i>=1;i--){
        while(!s.empty()&&st[s.top()].a>=st[i].a) s.pop();
        if(s.empty()) r[i]=n+1;
        else r[i]=s.top();
        s.push(i);
    }
    for(int i=1;i<=n;i++){
        len[i]=r[i]-l[i]-1;
    }
}

int main(){
    //freopen("a.txt","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;i++) { scanf("%d",&st[i].a); st[i].id=i; }
    init();
    sort(st+1,st+n+1,cmp);
    cnt=1;
    for(int i=1;i<=n&&(cnt!=(n+1));i++){
        while(cnt!=(n+1)&&len[st[i].id]>=cnt){
            printf("%d",st[i].a);
            if(cnt==n) printf("\n");
            else printf(" ");
            cnt++;
        }
    }
}

E.已知n个数a[i]。每次询问一个下标i,如果该下标已存在,则删除a[i],否则添加a[i]。求每个询问完成后,存在序列中两两互素的对数。
解法:已知a[1,2…n]两两互素的对数统计可以通过莫比乌斯变换得到。
观察式子可以得到更新后,只需要变化当前数的因子即可
结论:x的因子个数不超过2*sqrt(x)个。
由上述结论复杂度可以控制在q*sqrt(max(a[i])),时间复杂度可以接受。

#include <string>
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#define ll long long
using namespace std;

const ll maxn = 5000000+10;

ll n,q,x;
ll a[maxn];
ll mu[maxn];
bool not_prime[maxn],vis[maxn];
vector<ll> prime;
vector<ll> fac[maxn];
ll beinum[maxn],tmp;
ll ans;

void init(){
    mu[1]=1;
    memset(not_prime,0,sizeof(not_prime));
    for(ll i=2;i<=maxn-5;i++){
        if(!not_prime[i]) { prime.push_back(i); mu[i]=-1; }
        for(ll j=0;j<prime.size()&&i*prime[j]<=maxn-5;j++){
            not_prime[i*prime[j]]=1,mu[i*prime[j]]=-mu[i];
            if(!(i%prime[j])) { mu[i*prime[j]]=0; break; }
        }
    }
}

void init2(){
    for(ll i=1;i<=n;i++){
        ll x=a[i];
        for(ll j=1;j*j<=x;j++){
            if(x%j==0){
                fac[i].push_back(j);
                if(x/j!=j) fac[i].push_back(x/j);
            }
        }
    }
}

ll c2(ll x){
    return x*(x-1)/2;
}


void factor(ll id,ll x){
    if(vis[id]) tmp=-1; else tmp=1;
    for(ll i=0;i<fac[id].size();i++){
        ll j=fac[id][i];
        ans-=(mu[j]*c2(beinum[j]));
        ans+=(mu[j]*c2(beinum[j]+tmp));
        beinum[j]+=tmp;
    }
}

int main(){
    init();
    //freopen("a.txt","r",stdin);
    scanf("%I64d%I64d",&n,&q);
    for(ll i=1;i<=n;i++) scanf("%I64d",&a[i]);
    init2();
    ans=0;
    while(q--){
        scanf("%I64d",&x);
        factor(x,a[x]);
        if(vis[x]) vis[x]=0;
        else vis[x]=1;
        printf("%I64d\n",ans);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值