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;
}