分析:
一道很简单的签到题;只需要分奇数和偶数两种情况找规律即可。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while(t--){
int n,ans=0;cin>>n;
ans+=(n+1)/2;
if(n&1) ans+=(n-(n-1)/3+1)/2;
else ans+=(n-(n-1)/3)/2;
cout<<ans<<endl;
}
return 0;
}
1002.Time-division Multiplexing
分析:
注意到每个时隙的字符串长度不超过 12,因此所有时隙总的循环周期是所有字符串长度的 lcm,即最小公倍数,所以所有可能循环的字符串长度为lcm(1,2,3,4,5,6,7,8,9,10,11,12)=27720。也就是说对于这n个串来说,他们构成的最大的最小周期串长度是27720 * n;(n<100)。需要注意的是,这个字符串是一个有循环节的无限长的串,因此需要对两倍长度的循环节求解。所以当我们构造出了这个两倍长度的循环节串s以后,只需要用双指针法或者二分法求解即可。关键在于构造该字符串s。注意:该二分法枚举的是区间的长度,将区间整体右移。
AC代码如下:
二分法
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn=6e6+9;
int n,ans,kind,sum;
int pos[105],len[105],num[30];
bool vis[100];
string s,str[105];
void Init(){
memset(len,0,sizeof(len));
memset(pos,0,sizeof(pos));
s.clear();ans=INF;kind=sum=0;
for(int i = 0;i <= 26 ; i ++) vis[i]=false;
for(int i = 1;i <= n ; i ++){
cin>>str[i];
len[i]=str[i].length();
for(int j=0;j<len[i];j++){
if(!vis[str[i][j]-'a']){
vis[str[i][j]-'a']=true;
kind++;
}
}
}
}
inline bool checkS(){
for(int i=1;i<=n;i++){
if(pos[i]!=0) return true;
}
return false;
}
void creat(){
do{
for(int i=1;i<=n;i++){
s+=str[i][pos[i]];
pos[i]=(pos[i]+1)%len[i];
}
sum+=n;
}while(checkS());
s=s+s;
sum*=2;
}
bool check(int mid){
memset(num,0,sizeof(num));
int flag=0;
for(int i=0;i<mid;i++){
if(!num[s[i]-'a']) flag++;
num[s[i]-'a']++;
}
if(flag==kind) return true;
int l=0;
for(int r=mid;r<sum;r++,l++){
if(--num[s[l]-'a']==0) --flag;
if(++num[s[r]-'a']==1) ++flag;
if(kind==flag) return true;
}
return false;
}
int main(){
iostream::sync_with_stdio(false);
int t;cin>>t;
while(t--){
cin>>n;
Init(); creat();
int l=kind,r=sum;
while(l<r){
int mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
cout<<l<<endl;
}
return 0;
}
双指针法
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int maxn=6e6+9;
int n,ans,kind,sum;
int pos[105],len[105],num[105];
bool vis[100];
string s,str[105];
void Init(){
memset(len,0,sizeof(len));
memset(num,0,sizeof(num));
memset(pos,0,sizeof(pos));
s.clear();ans=INF;kind=sum=0;
for(int i = 0;i <= 26 ; i ++) vis[i]=false;
for(int i = 1;i <= n ; i ++){
cin>>str[i];
len[i]=str[i].length();
for(int j=0;j<len[i];j++){
if(!vis[str[i][j]-'a']){
vis[str[i][j]-'a']=true;
kind++;
}
}
}
}
inline bool checkS(){
for(int i=1;i<=n;i++){
if(pos[i]!=0) return true;
}
return false;
}
void creat(){
do{
for(int i=1;i<=n;i++){
s+=str[i][pos[i]];
pos[i]=(pos[i]+1)%len[i];
}
sum+=n;
}while(checkS());
s=s+s;
sum*=2;
}
int main(){
iostream::sync_with_stdio(false);
int t;cin>>t;
while(t--){
cin>>n;
Init(); creat();
int l=0,r=0,cnt=0;
while(l<sum&&r<sum){
while(r<sum&&cnt<kind){
if(num[s[r]-'a']==0) cnt++;
num[s[r++]-'a']++;
}
if(cnt!=kind) break;
while(l<r&&cnt==kind){
num[s[l]-'a']--;
if(num[s[l++]-'a']==0) cnt--;
}
ans=min(ans,r-l+1);
}
cout<<ans<<endl;
}
return 0;
}
分析:
通过找规律,可以发现一个等式 x^ 2-(x+1)^ 2-(x+2)^ 2+(x+3)^ 2==4;
所以也就是说字符串"1001"就是一个4;而我们也可以通过前四个数字平方的组合,也就是[1,4,9,16]构造出1,2,3的值。显然,1可以用“1”代替;2可以用"0001"代替,3可以用"01"代替。对于任意一个数x, x%4=[0,1,2,3];所以很显然我们可以通过字符串构造出所有的可能。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;cin>>t;
while(t--){
int n;cin>>n;
if(n%4==1) {
cout<<1+n/4*4<<endl;
cout<<"1";
}
else if(n%4==2) {
cout<<4+n/4*4<<endl;
cout<<"0001";
}
else if(n%4==3) {
cout<<2+n/4*4<<endl;
cout<<"01";
}
else cout<<n/4*4<<endl;
for(int i=1;i<=n/4;i++) cout<<"1001";
cout<<endl;
}
return 0;
}
分析:
对于 f(x)=Ax^ 2 g(x) +Bx^ 2+Cxg^ 2(x)+Dxg(x)来说,f(x)=(A*g(x)+B)x^ 2+(Cg^ 2(x)+Dg(x))x 。
因为x属于[1,1e6], g(x)max = g(999999) = 54 。也就是说g(x)只有54种不同的值。我们可以枚举g(x)的每一个取值,那么f(x)就是一个 普通的二次函数f(x)=a* x * x+b*x; 此时,只需要讨论一下a的取值情况(a>0,a=0,a<0);然后对于(a!=0)的二次函数求最小值来说,由于它不是单调问题,二分法不好用,但是由于它是有单峰值的函数,所以可以用三分法解决。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
typedef long long ll;
vector<ll>v[100];
ll A,B,C,D,n;
inline ll f(ll a,ll b,ll x){
return a*x*x+b*x;
}
inline ll g(ll x){
int res=0;
while(x){
res+=x%10;
x/=10;
}
return res;
}
void pre(){
for(ll i=1;i<=1e6;i++){
v[g(i)].push_back(i);
}
}
void solve(){
ll ans=1e18;
for(ll i=1;i<=54;i++){
ll l=0;
ll r=upper_bound(v[i].begin(),v[i].end(),n)-v[i].begin()-1; //找到g(x)=i的第一个大于n的数的前一个(最大的小于n的数)
if(r<0) continue;
ll a=A*i+B,b=C*i*i+D*i;
if(a<=0) ans=min(ans,min(f(a,b,v[i][0]),f(a,b,v[i][r])));
while(l<=r){
ll mid1=l+(r-l)/3;
ll mid2=r-(r-l)/3;
ll res1=f(a,b,v[i][mid1]),res2=f(a,b,v[i][mid2]);
if(res1<res2) r=mid2-1;
else l=mid1+1;
ans=min(ans,min(res1,res2));
}
}
cout<<ans<<endl;
}
int main(){
iostream::sync_with_stdio(false);
pre();
int t;cin>>t;
while(t--){
cin>>A>>B>>C>>D>>n;
solve();
}
return 0;
}
分析:
当机器人回到了原处,也就是说机器人走了一个周期。我们可以用二维数组mp[u][l]记录当前机器人的状态,也就是上下方向状态为u和左右方向状态为l。当两个机器人状态相同时,也就是走过了一个周期,那么答案ans++。需要注意的是,当状态为(u,l)的时候,如果前面的字符串中出现了x次(u,l)的状态,那么我们应该将ans+=x。所以我们可以用mp[u][l]来记录当前状态(u,l)出现的次数,也就是不断让mp[u][l]++。记住,由于n<=1e5,所以二维数组mp[][]应该用嵌套的map来维护。由于初态机器人位置为(0,0),需要将mp[0][0]初始化为1
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,map<int,int>>mp;
int main(){
int t;cin>>t;
while(t--){
mp.clear();mp[0][0]=1;
int n;cin>>n;
ll ans=0;
string s;cin>>s;
int u=0,l=0;
for(int i=0;i<n;i++){
if(s[i]=='U') u++;
else if(s[i]=='D') u--;
else if(s[i]=='L') l++;
else l--;
ans+=mp[u][l];
mp[u][l]++;
}
cout<<ans<<endl;
}
return 0;
}