noip模拟10
发现是前几天早上那场 MX(美熙) 的模拟赛,但是没做。
A 王国边缘
其实知道是倍增题。但是想不出来怎么个倍增法。
于是用分块打上了部分分 \(70\) 分。
我们用倍增设 \(f_{i,j}\) 表示从 \(i\) 开始移动 \(2^j\) 次之后到达的下标的相对位置,\(g_{i,j}\) 表示从 \(i\) 开始移动 \(2^j\) 次后到达的位置之前的完整周期个数。
然后对询问二进制拆分即可。
当然也可以把循环周期当作一个个有向边,在基环树上跑倍增。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
const int mod=1e9+7;
int n,q,m,s[N],t[60][N],t2[60][N];
signed main()
{
// ios::sync_with_stdio(false);
// cin.tie(0),cout.tie(0);
cin>>n>>m>>q;
int l=-m;
for(int i=0;i<n;i++)
{
char c;cin>>c;
if(c=='1') s[i]=i,l=i;
else s[i]=l;
}
if(l!=-m)
{
for(int i=0;i<n&&s[i]==-m;i++)s[i]=l-n;
}
for(int i=0;i<n;i++)
{
int k=(i+m)%n;
t[0][i]=max(i+m-k+s[k],i+1);
t2[0][i]=t[0][i]%n;
t[0][i]%=mod;
}
// cout<<"sss\n";
for(int i=1;i<60;i++)
{
for(int j=0;j<n;j++)
{
int k=t2[i-1][j];
t2[i][j]=t2[i-1][k];
t[i][j]=(t[i-1][k]-k+t[i-1][j])%mod;
}
}
while(q--)
{
int s,k,s2;
cin>>s>>k;
s--;s2=s%n;
for(int i=0;k;k>>=1,i++)
{
if(k&1)
{
s=(t[i][s2]+s-s2)%mod;
s2=t2[i][s2];
}
}
cout<<(s+1)%mod<<"\n";
}
}
B 买东西题
知道是贪心,但是不知道怎么贪。。。
考场上想到了维护每个数的价差 \(c_i=a_i-b_i\),并且知道对于一个所有可用的优惠券集合,匹配最优的一定是价差最大的与优惠券减价最多的。
但是没想到可以看作每个数的价差也应该视作一张优惠券(其实猜到了,没敢想),一起维护在优惠券集合内,当你现在要反悔,第 \(i\) 个点要拿走第 \(j\) 个点的优惠券,那么他应该交换而不是直接拿。就是把 \(i\) 的价差维护进优惠券集合,拿走 \(j\) 的优惠券,就好了。
为了保证优惠券一定可用,就需要把优惠券和商品分别按 \(a\) 升序排列,依次将前缀的优惠券加入优先队列中维护。
和正解就差一行代码!!!
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m;
const int N=1e6+5;
struct node1{
int a,b;
inline bool operator<(const node1 &ll) const{
if(ll.b==b) return ll.a>a;
return ll.b>b;
}
}c[N];
struct node{
int a,b,c;
inline bool operator<(const node &ll) const{
if(ll.c==c) return ll.a>a;
if(ll.a==a) return ll.b>b;
return ll.c<c;
}
}e[N];
inline bool cmp(node a,node b)
{
// if(a.a==b.a) return a.b>b.b;
// return min(a.a,b.a)>min(a.b,b.b);
if(a.a==b.a) return a.b>b.b;
return a.a<b.a;
}
inline bool cmp1(node1 a,node1 b)//实际上这几个是一样的,只是在不同的部分分和结构体内。
{
if(a.a==b.a) return a.b>b.b;
return a.a<b.a;
}
inline bool cmp2(node a,node b)
{
// if(a.a==b.a) return a.b>b.b;
// return min(a.a,b.a)>min(a.b,b.b);
if(a.a==b.a) return a.b<b.b;
return a.a<b.a;
}
inline bool cmp3(node1 a,node1 b)
{
if(a.a==b.a) return a.b<b.b;
return a.a<b.a;
}
priority_queue<node1>q;
priority_queue<node>q1;//实际上没有用到它
signed main()
{
// freopen("buy5.in","r",stdin);
freopen("buy.in","r",stdin);
freopen("buy.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
bool _=1;
for(int i=1;i<=n;i++) cin>>e[i].a>>e[i].b,e[i].c=(e[i].a-e[i].b),_&=(e[i].a==e[i].b);
for(int i=1;i<=m;i++) cin>>c[i].a>>c[i].b;
if(_||n<=10)
{
sort(e+1,e+1+n,cmp);
sort(c+1,c+1+m,cmp1);
int ans=0,cnt=0;
for(int i=1,j=1;i<=n;i++)
{
while(c[j].a<=e[i].a&&j<=m)q.push(c[j++]);
// cout<<j<<" ";
if(!q.empty()&&e[i].a-q.top().b<e[i].b) ans+=e[i].a-q.top().b,q.pop(),++cnt;
else ans+=e[i].b;
// cout<<"c: "<<c[i].a<<" "<<c[i].b<<"\n";
// cout<<"e: "<<e[i].a<<" "<<e[i].b<<"\n";
// cout<<ans<<"\n";
}
cout<<ans;return 0;
}
sort(e+1,e+1+n,cmp2);
sort(c+1,c+1+m,cmp3);
int ans=0,cnt=0,j=1;
for(int i=1;i<=n;i++)
{
while(j<=m&&c[j].a<=e[i].a) q.push(c[j++]);
if(q.empty()||e[i].c>q.top().b) ans+=e[i].b;
else ans+=e[i].a-q.top().b,q.pop(),q.push({1,e[i].a-e[i].b});//就是这个 push 没写出来
}
// for(int i=1;i<=n;i++) q1.push(e[i]);
// for(int i=1;i<=m;i++) q.push(c[i]);
// while(!q1.empty())
// {
// if(q.top().b<q1.top().c) ans+=q1.top().b,q1.pop();
// else ans+=q1.top().a-q.top().b,q.pop(),q1.pop();
// }
cout<<ans;//其他的都是奇奇怪怪的假做法和部分分。
// for(int i=1;i<=n;i++) cout<<"q "<<q1.top().a<<" "<<q1.top().b<<"\n",q1.pop();
// return 0;
// for(int i=1;i<=m;i++)
// {
// while(c[i].a<=e[j].a&&j<=n)q1.push(e[j++]);
//// cout<<i<<" ";
//// cout<<q1.top().a<<" "<<q1.top().b<<"\n";
//// cout<<"c: "<<c[i].a<<" "<<c[i].b<<"\n";
// q.push(c[i]);
// if(!q1.empty()&&q1.top().c<q.top().b)ans+=q1.top().a-q.top().b,q1.pop(),++cnt,q.pop();
// else if(!q1.empty())ans+=q1.top().b,q1.pop();
//// cout<<"ans: "<<ans<<"\n";
// }
//// cout<<ans<<"\n";
//// cout<<j<<"\n";
// for(int i=j;i<=n;i++) q1.push(e[i]);
// while(!q1.empty()) ans+=q1.top().b,q1.pop();
// cout<<ans;
}
C IMAWANOKIWA (Construction ver.)
分讨+构造。
首先,对于 ans1,有结论:
-
当且仅当 \(a\) 全为 \(0\) 时答案为 \(0\);
-
序列 \(a\) 没有 \(0\) 时答案为 \(2\) 的个数对 \(2\) 取模的余数+1;
-
否则,当且仅当序列 \(a\) 为 \(1,1,1,1...1,2,0,2,1,...1,1,1\)(其中 \(1\) 可用没有)的形式时,答案为 \(2\),否则为 \(1\)。
还有结论,第一个能合并的位置移动不超过 \(3\)。
那么就可以每次合并前几个,用上面的三个结论试一下答案是否会被改变,如果没有那么保留。
好像有 \(61\) 分。
tj:
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ull unsigned long long
const int N=100007,op[3][3]={{0,1,1},{1,1,2},{1,2,1}};
const ull pp=13331;
int T,n,a[N],k,p,last0;
char s[N];
ull ans;
signed main()
{
freopen("popc.in","r",stdin);
freopen("popc.out","w",stdout);
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>T;
while(T--)
{
cin>>(s+1);
n=strlen(s+1);
k=a[1]=s[1]-'0',ans=0;
for(int i=2;i<=n;++i)
a[i]=s[i]-'0',k=op[k][a[i]];
if(k<2)
{
cout<<k<<" ";
for(int i=1;i<n;++i) ans=ans*pp+1;
cout<<ans<<"\n";
continue;
}
last0=0;
for(int i=n;i;--i)
{
if(!a[i])
{
last0=i;break;
}
}
if(!last0)
{
cout<<k<<" ";
for(int i=1;i<n;++i) ans=ans*pp+1;
cout<<ans<<"\n";
continue;
}
bool fl=0;
for(int i=1;i<last0;++i) fl|=(bool)a[i];
if(!fl)
{
cout<<"1 ";
k=a[last0+1];p=last0+1;
for (int i=1;i<last0;++i)
ans=ans*pp+1;
while(k==a[last0+1])
{
ans=ans*pp+2;k=op[k][a[++p]];
}
for(int i=0;i<=n-p;++i)
ans=ans*pp+1;
cout<<ans<<'\n';
continue;
}
k=a[1];
for(int i=2;i<last0;++i)
{
ans=ans*pp+1,k=op[k][a[i]];
}
int tmp=a[last0+1],p=last0+1;
while(k==tmp&&p<=n)
{
tmp=op[tmp][a[++p]];ans=ans*pp+3;
}
if(p<=n)
{
ans=ans*pp+2;
for(int i=0;i<=n-p;++i) ans=ans*pp+1;
cout<<"1 "<<ans<<'\n';continue;
}
ans=0;
tmp=a[last0-1];p=last0-1;
while(tmp!=1&&p)tmp=op[tmp][a[--p]];
if(!p)
{
cout<<"2 ";
for(int i=1;i<n;++i) ans=ans*pp+1;
cout<<ans<<'\n';
continue;
}
cout<<"1 ";
k=a[1];
for(int i=2;i<p;++i)
{
k=op[k][a[i]],ans=ans*pp+1;
}
if(a[p]==0)
{
if(k==1)
{
for(int i=2;i<last0-p;++i) ans=ans*pp+3;
ans=((ans*pp+2)*pp+1)*pp+2;
for(int i=1;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
else if(k==2)
{
if(last0==p+2)
{
ans=(ans*pp+2)*pp+2;
for(int i=1;i<n-p;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
else
{
ans=ans*pp+2;
for(int i=1;i<last0-p;++i) ans=ans*pp+1;
ans=ans*pp+2;
for(int i=1;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
}
else
{
if(p>1) ans=ans*pp+1;
if(last0==p+2)
{
ans=(ans*pp+1)*pp+2;
for(int i=1;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
else
{
for(int i=2;i<last0-p;++i) ans=ans*pp+2;
ans=(ans*pp+1)*pp+2;
for(int i=1;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
}
}
else
{
if(k==2)
{
for(int i=1;i<=last0-p;++i) ans=ans*pp+2;
for(int i=0;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
else
{
for(int i=1;i<last0-p;++i) ans=ans*pp+2;
ans=(ans*pp+1)*pp+2;
for(int i=1;i<=n-last0;++i) ans=ans*pp+1;
cout<<ans<<'\n';
}
}
}
return 0;
}