说实在的,中国人出的题果然很(du)好(liu)。
◇题目传送门◆
给定一个值 x x x,有四种等级与 x x x按照如下对应方式对应:
其中等级 A A A最高,等级 D D D最低。现可以将 x x x变成 x , x + 1 , x + 2 x,x+1,x+2 x,x+1,x+2,求能达到的最高等级以及其加上的数。
水题。暴力判断即可。
#include
#include
using namespace std;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int n;
scanf("%d",&n);
int ans=0;
char ansch='E';
for(int i=0;i<=2;i++) {
int tmp=(n+i)%4;
char ch;
if(tmp==0)ch='D';
if(tmp==1)ch='A';
if(tmp==2)ch='C';
if(tmp==3)ch='B';
if((int)ch<(int)ansch) {
ansch=ch;
ans=i;
}
}
printf("%d %c\n",ans,ansch);
return 0;
}
◇题目传送门◆
给定一种麻将,麻将牌由若干1m,2m,3m,...,9m,1p,2p,3p,...,9p,1s,2s,3s,...9s
组成,现在手中有三张牌,要求再找一些牌,使得手中的牌满足以下两个条件中的一个:
求最少要找的牌的数量。
细节题。。。细节特别多,写的时候要特别注意。
#include
#include
#include
#include
#include
using namespace std;
string s[5];
map<string,int> t;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
for(int i=1;i<=3;i++) {
cin>>s[i];
t[s[i]]++;
}
if(t[s[1]]==3) {
puts("0");
return 0;
}
int ans=5;
for(int i=1;i<=3;i++) {
if(t[s[i]]==2) {
ans=min(ans,1);
}
string tmp=s[i];
tmp[0]++;
bool flag=true;
int cnt=2;
if(tmp[0]>'9')flag=false;
else if(t.count(tmp))cnt--;
tmp[0]++;
if(tmp[0]>'9')flag=false;
else if(t.count(tmp))cnt--;
if(flag)ans=min(ans,cnt);
tmp=s[i],cnt=2,flag=true;
tmp[0]--;
if(tmp[0]<'1')flag=false;
else if(t.count(tmp))cnt--;
tmp[0]--;
if(tmp[0]<'1')flag=false;
else if(t.count(tmp))cnt--;
if(flag)ans=min(ans,cnt);
}
printf("%d\n",ans);
return 0;
}
◇题目传送门◆
给定 N N N,表示从 1 1 1到 N N N这些数排在一串,现将这些数按照 K K K个分成一段一段的。
给出 M M M个数字,要求从数列中删掉这些数,每次删除能够将一段中的所有要删的数删完。删除后后面的数自动向前移动,占用前面的数的位置。求删数的次数。
我们发现,只要删掉了多少个数,后面的数就会向前平移多少个位置,所以我们只需记录下这个向前平移的位置长度,再在向后查询出同一段的数的个数即可。
#include
#include
using namespace std;
typedef long long ll;
const int Maxm=1e5;
ll N,M,K;
ll p[Maxm+5];
ll o=0;
int BS(ll val,int lb,int ub) {
while(lb+1<ub) {
int mid=(lb+ub)>>1;
if(p[mid]-o==val)
return mid;
if(p[mid]-o<val)
lb=mid;
else ub=mid;
}
return lb;
}
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%lld %lld %lld",&N,&M,&K);
for(int i=1;i<=M;i++)
scanf("%lld",&p[i]);
int ans=0;
for(int i=1;i<=M;) {
ll k=((p[i]-o)%K==0?(p[i]-o)/K-1:(ll)(p[i]-o)/K);
ll st=k*K+1;
int pos=BS(st+K-1,i,M+1);
o=pos;
i=pos+1;
ans++;
}
printf("%d\n",ans);
return 0;
}
◇题目传送门◆
给定 N N N堆石子,每堆有 a i a_i ai个,两名玩家可以轮流从任意一堆石子中取走 1 1 1颗石子,若某玩家不能操作,则该玩家输了;若某玩家取完石子后,有两堆石子数量相同,则这个玩家输了。求哪个玩家胜利。
不难发现最终状态必定为 0 , 1 , … , ( N − 1 ) 0,1,\ldots,(N-1) 0,1,…,(N−1)的一个排列,若这些数的和为偶数,则后手胜利,否则先手胜利。
那么我们考虑特殊情况,在先手取走一颗后,设某堆数量为 x x x的石块出现次数为 c [ x ] c[x] c[x]次,那么:
所以我们就只需判断完这些特殊情况后,按常规方法判断即可。
#include
#include
#include
using namespace std;
const int Maxn=1e5;
int N,A[Maxn+5];
map<int,int> cnt;
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
bool flag=true;
for(int i=1;i<=N;i++) {
scanf("%d",&A[i]);
cnt[A[i]]++;
flag&=(cnt[A[i]]<3);
}
int tmp=0;
for(map<int,int>::iterator it=cnt.begin();it!=cnt.end();it++)
if(it->second>=2)tmp++;
flag&=(tmp<=1);
for(map<int,int>::iterator it=cnt.begin();it!=cnt.end();it++)
if(it->second==2) {
flag&=(cnt.count(it->first-1)==0);
if(it->first==0)flag=false;
}
if(flag==false) {
puts("cslnb");
return 0;
}
sort(A+1,A+N+1);
long long s=0;
for(int i=1;i<=N;i++)
s+=(A[i]-(i-1));
puts(s%2==0?"cslnb":"sjfnb");
return 0;
}
◇题目传送门◆
给定一个 01 01 01串,每次能将连续 K K K个字符中的所有 0 0 0变成 1 1 1,或者将所有 1 1 1变成 0 0 0。谁先使所有的字符变成相同的谁胜利。求谁胜利或者平局。
怎么有两个博弈题???
简单分析可以发现,先手必须在一步之内完成比赛,否则他就会输掉,而后手也必须在一步以内结束比赛,否则就是一个平局。(这想出来也很烦。。。实现几乎没难度。。。)
程序按照这样写就是了。。。
#include
#include
#include
#include
using namespace std;
const int Maxn=1e5;
int N,K,len;
char s[Maxn+5];
int L[Maxn+5],R[Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d %d\n%s",&N,&K,s+1);
for(int i=1,tmp[2]={-1,-1};i<=N;i++) {
int t=s[i]-'0';
L[i]=tmp[t^1];
tmp[t]=i;
}
for(int i=N,tmp[2]={N+1,N+1};i>=1;i--) {
int t=s[i]-'0';
R[i]=tmp[t^1];
tmp[t]=i;
}
bool First=false,Second=true;
for(char op='0';op<='1'&&First==false;op++) {
vector<int> tmp;
for(int i=1;i<=N;i++)
if(s[i]!=op)
tmp.push_back(i);
for(int l=1,r=K,tl=0,tr=-1;r<=N;l++,r++) {
while(tl<tmp.size()&&tmp[tl]<l)tl++;
while(tr+1<tmp.size()&&tmp[tr+1]<=r)tr++;
if(tl==0&&tr==tmp.size()-1) {
First=true;
break;
}
int l1=tmp[tl?0:tr+1],r1=tmp[tr<tmp.size()-1?tmp.size()-1:tl-1];
int l2=l1>1?1:min(R[l1],r1+1),r2=l2<N?N:min(L[r1],l1-1);
Second&=(r1-l1+1<=K|r2-l2+1<=K);
}
}
puts(First?"tokitsukaze":(Second?"quailty":"once again"));
return 0;
}
◇题目传送门◆
给定一些点(坐标为正整数),要求求出用直线 x = l , x = r , y = a ( l < r ) x=l,x=r,y=a(l<r) x=l,x=r,y=a(l<r)(其中 l , r , a l,r,a l,r,a可以是任何数)围成的开口向上区域中的点构成的集合中,不同的个数。
这应该是Div.2 D难度吧。。。
考虑先画出 y = a y=a y=a这条线(相比于先构造 x = l , x = r x=l,x=r x=l,x=r这两条直线,我们不需要考虑区间问题)(因为这样的话更为简单)。
不难想出如下算法:
对于某个点 ( x , y ) (x,y) (x,y),我们找出它的左边的点且纵坐标比它大的点的数量 l l l和在它右边且纵坐标比它大的点的数量 r r r,这样我们只需讨论由这些点构成的集合数量:
所以这个点总方案数为 1 + l + r + l × r 1+l+r+l\times r 1+l+r+l×r。
我们在统计右边的点的时候,会统计到这种情况,在统计到左边的点的时候,会统计到同一种情况,这样就是重复的。所以我们在查找的时候,若两点纵坐标相同,则 r r r只查询两点之间的点的数量。
为了保证时间和空间,我们采用树状数组维护区间横坐标,并采用离散化处理所有点坐标。
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int Maxn=2e5;
struct BIT {
int s[Maxn+5];
int siz;
inline int lowbit(int x){return x&(-x);}
void add(int x,int val) {
while(x<=siz) {
s[x]+=val;
x+=lowbit(x);
}
}
inline int _sum(int x) {
if(x<=0)return 0;
int ret=0;
while(x>0) {
ret+=s[x];
x-=lowbit(x);
}
return ret;
}
int sum(int l,int r) {
if(l>r)return 0;
return _sum(r)-_sum(l-1);
}
};
struct Point {
int x,y;
bool operator < (const Point &rhs) const {return y==rhs.y?x<rhs.x:y>rhs.y;}
};
Point A[Maxn+5];
int N;
BIT cnt;
int Xsiz,Ysiz;
void Prepare() {
vector<int> x,y;
for(int i=1;i<=N;i++) {
x.push_back(A[i].x);
y.push_back(A[i].y);
}
sort(x.begin(),x.end());
sort(y.begin(),y.end());
x.resize(unique(x.begin(),x.end())-x.begin());
y.resize(unique(y.begin(),y.end())-y.begin());
cnt.siz=Xsiz=x.size(),Ysiz=y.size();
for(int i=1;i<=N;i++) {
A[i].x=lower_bound(x.begin(),x.end(),A[i].x)-x.begin()+1;
A[i].y=lower_bound(y.begin(),y.end(),A[i].y)-y.begin()+1;
}
}
bool is_add[Maxn+5];
int main() {
#ifdef LOACL
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
scanf("%d",&N);
for(int i=1;i<=N;i++)
scanf("%d %d",&A[i].x,&A[i].y);
Prepare();
sort(A+1,A+N+1);
ll ans=0;
for(int i=1;i<=N;i++) {
int l=cnt.sum(1,A[i].x-1),r;
if(A[i].y==A[i+1].y)r=cnt.sum(A[i].x+1,A[i+1].x-1);
else r=cnt.sum(A[i].x+1,Xsiz);
ans+=(1+l+r+1LL*l*r);
if(is_add[A[i].x]==false)cnt.add(A[i].x,1),is_add[A[i].x]=true;
}
printf("%lld\n",ans);
return 0;
}