发现是前几天早上那场 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;
}