Preface
军训终于结束了回来补一补之前的坑发现很多题目题意都忘记了
这场感觉难度适中,F由于智力不够所以弃了,E的话石乐志看了官方英文题解才发现自己已经胡了一大半就差实现了233
水平下降严重.jpg
A. Tokitsukaze and Discard Item
CNM我SB模拟题要WA那么多发还是退役算了万恶的long long
#include<cstdio>
#define RI register int
#define CI const int&
const int N=100005;
int m,ans; long long n,k,a[N],id,nw;
int main()
{
RI i,j; for (scanf("%I64d%d%I64d",&n,&m,&k),i=1;i<=m;++i)
scanf("%I64d",&a[i]); for (i=1;i<=m;)
for (id=(a[i]-(i-1)-1)/k+1,j=i+1;j<=m+1;++j)
if (j==m+1||(nw=(a[j]-(i-1)-1)/k+1)!=id) { ++ans; i=j; break; }
return printf("%d",ans),0;
}
B. Tokitsukaze, CSL and Stone Game
本来想和陈指导比一下看谁WA的多的,最后我以多一次的好成绩力压了他
这题比较繁琐,大体思路就是先判断第一个人是否必输,这个需要考虑各种情况,具体看代码
然后我们发现接下来每个数能被拿的数量数固定的,而且在着之上双方都有保证自己操作合法的方法,因此统计下能拿的数目的总数判断下奇偶性即可
#include<cstdio>
#include<map>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,a[N],num_mx,tot; map <int,int> ct; long long sum;
int main()
{
RI i; for (scanf("%d",&n),i=1;i<=n;++i)
{
scanf("%d",&a[i]);
if (++ct[a[i]]>num_mx) num_mx=ct[a[i]];
}
if (ct[0]>=2) return puts("cslnb"),0;
if (ct[0]&&ct[0]+ct[1]==n) return puts("cslnb"),0;
if (num_mx>=3) return puts("cslnb"),0;
for (map <int,int>::iterator it=ct.begin();it!=ct.end();++it)
if (it->second>=2) ++tot; if (tot>=2) return puts("cslnb"),0;
for (i=1;i<=n;++i) if (ct[a[i]]>=2&&ct[a[i]-1]) return puts("cslnb"),0;
/*bool flag=0; for (i=1;i<=n;++i) if (a[i]&&!ct[a[i]-1])
{ flag=1; break; } if (!flag) return puts("cslnb"),0;*/
for (i=1;i<=n;++i) sum+=a[i]-i+1; return puts(sum&1?"sjfnb":"cslnb"),0;
}
C. Tokitsukaze and Duel
又是坑提交的博弈题,发现我猜起结论来倒是挺NB的
首先判断第一个人能否一击必胜,否则它要么输掉要么平局(否则另一个人至少可以不断操作和它一样的区间来抵消影响)
那么接下来我们考虑枚举第一个人的所有操作情况,如果任一情况下没有导致必胜态出现就能打成平局,否则就是输掉(结合SG函数的定义理解一下)
那么我们到底需不需要枚举每一种情况呢,容易发现如果染中间的某一段一旦出现了非同色点在区间两边的情况就必然不可能输掉,因此只要判断染两个端点的即可
#include<cstdio>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
const int N=100005;
int n,k,pfx[N],tp,all; char s[N],ts[N];
inline bool is_win(char *s,CI n)
{
RI i; for (all=0,i=1;i<=n;++i) pfx[i]=pfx[i-1]+s[i]-'0',all+=s[i]-'0';
for (i=1;i+k-1<=n;++i) if (tp=all-(pfx[i+k-1]-pfx[i-1]),!tp||tp==n-k)
return 1; return 0;
}
int main()
{
RI i; scanf("%d%d%s",&n,&k,s+1); if (is_win(s,n)) return puts("tokitsukaze"),0;
for (memcpy(ts,s,sizeof(s)),i=2;i<=k+1;++i) ts[i]='0';
if (!is_win(ts,n)) return puts("once again"),0;
for (memcpy(ts,s,sizeof(s)),i=2;i<=k+1;++i) ts[i]='1';
if (!is_win(ts,n)) return puts("once again"),0;
for (memcpy(ts,s,sizeof(s)),i=2;i<=k+1;++i) ts[n-i+1]='0';
if (!is_win(ts,n)) return puts("once again"),0;
for (memcpy(ts,s,sizeof(s)),i=2;i<=k+1;++i) ts[n-i+1]='1';
if (!is_win(ts,n)) return puts("once again"),0;
return puts("quailty"),0;
}
D. Tokitsukaze and Strange Rectangle
套路题,考虑每个点作为底边的贡献,这样我们就可以把点按\(y\)从大到小排序,然后一起考虑同一层
首先我们发现如果这一层只有一个点时,只要统计出所有不同的\(x\)坐标的种类数然后计算点对即可
那么多个点呢,考虑中间的点只会被两端重复计算,因此直接容斥减去中间的情况即可
离散化之后用树状数组维护点数目即可
#include<cstdio>
#include<vector>
#include<algorithm>
#define RI register int
#define CI const int&
using namespace std;
const int N=200005;
struct Point
{
int x,y;
friend inline bool operator < (const Point& A,const Point& B)
{
return A.y!=B.y?A.y>B.y:A.x<B.x;
}
}p[N]; int n,rst[N],num; long long ans;
inline long long S(CI x)
{
return 1LL*x*(x+1)>>1LL;
}
class Tree_Array
{
private:
int bit[N]; bool vis[N];
#define lowbit(x) x&-x
inline int get(RI x,int ret=0)
{
for (;x;x-=lowbit(x)) ret+=bit[x]; return ret;
}
public:
inline void add(RI x)
{
if (vis[x]) return; for (vis[x]=1;x<=num;x+=lowbit(x)) ++bit[x];
}
inline int query(CI l,CI r)
{
if (l>r) return 0; return get(r)-get(l-1);
}
#undef lowbit
}BIT;
int main()
{
RI i,j,k; for (scanf("%d",&n),i=1;i<=n;++i)
scanf("%d%d",&p[i].x,&p[i].y),rst[i]=p[i].x;
sort(rst+1,rst+n+1); num=unique(rst+1,rst+n+1)-rst-1;
for (i=1;i<=n;++i) p[i].x=lower_bound(rst+1,rst+num+1,p[i].x)-rst;
for (sort(p+1,p+n+1),p[n+1].x=num,p[n+1].y=1e9+1,i=1;i<=n;i=j+1)
{
for (j=i;p[j].y==p[j+1].y;++j);
for (k=i;k<=j;++k) BIT.add(p[k].x); ans+=S(BIT.query(1,num));
for (k=i+1;k<=j;++k) ans-=S(BIT.query(p[k-1].x+1,p[k].x-1));
ans-=S(BIT.query(1,p[i].x-1)); ans-=S(BIT.query(p[j].x+1,num));
}
return printf("%I64d",ans),0;
}
E. Tokitsukaze and Explosion
看到这题之后就随便口胡了一个做法,然后就基本正解了?
首先我们转化问题,考虑以原点为圆心做一个半径为\(d\)的圆,那么所有直线不能与圆相交
显然半径\(d\)可以二分,再考虑已知半径的时候直线必然与圆相切,因为不相切的直线向圆心平移显然不会更劣
然后我们就可以得出一条直线所能覆盖的一个极角范围了,因此原问题转化为能否用\(m\)条线段覆盖环上的\(n\)个点
考虑破环为链,我们枚举起点之后看看贪心地向后覆盖\(m\)条线段后能否绕回这个点
但是直接这么做显然时\(O(n^2)\)的,然后稍微一想就会发现可以倍增处理出每个点向后跳\(2^i\)后最远能到达的点,然后再枚举起点就可以了
复杂度\(O(n\log^2 n)\),可以轻松地跑过
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<cstring>
#define RI register int
#define CI const int&
using namespace std;
typedef double DB;
const int N=100005,P=20,INF=2e9;
const DB EPS=1e-10,tpi=2.0*acos(-1);
struct segment
{
DB l,r; int tp;
inline segment(const DB& X=0,const DB& Y=0,CI Tp=0)
{
l=X; r=Y; tp=Tp;
}
friend inline bool operator < (const segment& A,const segment& B)
{
return A.tp!=B.tp?A.tp<B.tp:A.l<B.l;
}
}sg[N<<1]; int n,m,tot,x[N],y[N],nxt[N<<1][P];
DB dis[N],mi=INF,ans; bool c[N<<1];
inline bool check(const DB& lim)
{
RI i,j; for (tot=0,i=1;i<=n;++i)
{
DB deg=atan2(y[i],x[i]),rg=acos(lim/dis[i]);
sg[++tot]=segment(deg-rg,deg+rg,0);
sg[++tot]=segment(deg-rg+tpi,deg+rg+tpi,1);
}
DB mr=INF; memset(c,1,tot+1);
for (sort(sg+1,sg+tot+1),i=tot;i;--i)
if (sg[i].r<mr) mr=sg[i].r; else c[i]=0;
for (tot=0,i=1;i<=n;++i) if (c[i]) sg[++tot]=sg[i];
for (i=1;i<=tot;++i) sg[tot+i]=segment(sg[i].l+tpi,sg[i].r+tpi,1);
for (tot<<=1,i=j=1;i<=tot;++i)
{
while (j<=tot&&sg[i].r>=sg[j].l) ++j;
nxt[i][0]=j;
}
for (j=1;j<P;++j) for (i=1;i<=tot;++i)
nxt[i][j]=nxt[nxt[i][j-1]][j-1];
for (i=1;(i<<1)<=tot;++i)
{
int p=i; for (j=0;j<P;++j) if ((m>>j)&1) p=nxt[p][j];
if (!p||p>=i+(tot>>1)) return 1;
}
return 0;
}
int main()
{
RI i; for (scanf("%d%d",&n,&m),i=1;i<=n;++i)
scanf("%d%d",&x[i],&y[i]),dis[i]=sqrt(1LL*x[i]*x[i]+1LL*y[i]*y[i]),mi=min(mi,dis[i]);
DB l=0,r=mi,mid; while (r-l>EPS)
if (check(mid=(l+r)/2.0)) ans=mid,l=mid; else r=mid;
return printf("%.10lf",ans),0;
}
F. Tokitsukaze and Powers
题意都那么麻烦就咕咕咕了
Postscript
一场比赛前几题都是博弈,这对于我来说就是罚时起飞的节奏
看来我还是适合做套路题233