感觉难度比去年大,也可能是我变菜了
第一场
1001迷失
1003 鸽子
1004 萌新
void solve(){
ll a=read,b=read;
if(b>a) swap(a,b);
ll tmp=a-b;
ll res_max=a-b,res_min=0;
if(!tmp) res_max=a,res_min=2;
rep(i,2,tmp/i){
if(tmp%i==0){
res_min=i;break;
}
}
if(!res_min) res_min=tmp;
if(res_min<=1||res_max<=1){
printf("-1 -1\n");
return ;
}
printf("%lld %lld\n",res_min,res_max);
}
int main(){
int _=read;
while(_--) solve();
return 0;
}
1006 毒瘤数据结构题
- 思路:
首先对于查询操作,是满足单调性的,我们可以二分答案,那么怎么 c h e c k check check呢,如果对于当前的 m i d mid mid, [ 1 , m i d − 1 ] [1,mid-1] [1,mid−1]的和为 m i d − 1 mid-1 mid−1,说明该点符合题意,还可以更大, l l l指针右移。
对于修改操作,相当于单点修改。
大体思路就是对答案进行二分,借用某数据结构维护单点修改和区间求和。
容易想到的是线段树跟树状数组,赛时是用树状数组写的,复杂度 O ( n l o g 2 n ) O(nlog^{2}n) O(nlog2n)
交了后宇巨说第二个查询的答案一定是单调递增的,所以应该有 O ( n ) O(n) O(n)的写法。
然后 h d u o j hduoj hduoj评测机的问题导致 O ( n ) O(n) O(n)的写法都会 T L E TLE TLE,知道思想就好了。 - 代码:
const int maxn=5e6+7,maxm=210000;
ll n,a[maxn],tr[maxn];
ll lowbit(ll x){
return x&-x;
}
void update(ll pos,ll val){
while(pos<=n){
tr[pos]+=val;
pos+=lowbit(pos);
}
}
ll query(ll pos){
ll res=0;
while(pos){
res+=tr[pos];
pos-=lowbit(pos);
}
return res;
}
bool check(int x){
int t=query(x-1);
if(t!=x-1) return 0;
return 1;
}
int main(){
n=read;
rep(i,1,n){
int op=read,x=read;
if(op==1&&a[x]!=1) a[x]=1,update(x,1);
else if(op==2){
//rep(j,1,n){
// cout<<a[j]<<" ";
//}
//puts("");
if(a[x]!=1) update(x,1);
int l=1,r=n,res;
while(l<=r){
int mid=(l+r)/2;
if(check(mid)){
res=mid;l=mid+1;
}
else r=mid-1;
}
//cout<<i<<"******";
printf("%d\n",res);
if(a[x]!=1) update(x,-1);
}
}
return 0;
}
1008 猎人杀
- 思路:
模拟;要注意的点为:
1. 1. 1.狼人可以杀死自己,猎人不可以杀死自己
2. 2. 2.注意每次杀人的时候都是选择最靠前并且未死亡的,所以每次要从 1 1 1开始遍历 - 代码:
int a[55],now[55],st[55];
int w[55][55];
int main(){
int _=read;
while(_--){
int n=read,pos;
rep(i,1,n){
a[i]=read;
if(a[i]) pos=i;
now[i]=1;st[i]=0;
}
rep(i,1,n){
rep(j,1,n){
w[i][j]=read;
}
}
int cnt=n;
int id=pos,flag=0;
while(1){
now[id]=1;
while(now[id]<=n&&st[w[id][now[id]]]) now[id]++;
st[w[id][now[id]]]=1;// die
int t=w[id][now[id]];
//cout<<now[id]<<"*****"<<t<<endl;
if(t==pos){
flag=1;break;
}
else{
id=t;cnt--;
}
if(cnt<=2){
flag=0;break;
}
}
if(flag) puts("lieren");
else puts("langren");
}
return 0;
}
第二场
1001 签到
- 思路
找规律,写出前 7 7 7项就好了
项数 | a | b |
---|---|---|
0 | a | b |
1 | a+b | a-b |
2 | 2a | 2b |
3 | 2a+2b | 2a-2b |
4 | 4a | 4b |
5 | 4a+4b | 4a-4b |
6 | 8a | 8b |
7 | 8a+8b | 8a-8b |
分奇偶讨论一下就好了,注意取模。
- 代码:
const ll mod=998244353 ;
int main(){
int _=read;
while(_--){
ll a=read,b=read,k=read;
if(k%2==0){
int t=k/2;
ll tmp=ksm(2,t,mod);
printf("%lld %lld\n",tmp*a%mod,tmp*b%mod);
}
else{
int t=(k-1)/2;
ll tmp=ksm(2,t,mod);
ll x=(a-b+mod)%mod;
printf("%lld %lld\n",tmp*(a+b)%mod,tmp*x%mod);
}
}
return 0;
}
1002 随机题意
- 思路:
对于每个 b [ i ] b[i] b[i]的范围为 [ a i − k , a i + k ] [a_{i}-k,a_{i}+k] [ai−k,ai+k]
排序后从头开始扫,贪心的选择。
每次都尽量选择每个区间最左边的。 - 代码:
int n,a[maxn],k;
struct node{
int l,r;
}b[maxn];
bool cmp(node a,node b){
if(a.l==b.l) return a.r<b.r;
return a.l<b.l;
}
int main(){
int _=read;
while(_--){
n=read,k=read;
rep(i,1,n){
a[i]=read;
b[i].l=a[i]-k,b[i].r=a[i]+k;
}
sort(b+1,b+1+n,cmp);
int res=1,now=b[1].l+1;
rep(i,2,n){
if(now<=b[i].r){
now=max(now+1,b[i].l+1);
res++;
}
}
printf("%d\n",res);
}
return 0;
}
1003 魔怔
1004 净化
- 思路:
首先,尽量找到分界点,在分界点之后,所有的负数都不会使得遍历完的答案减少,也就是说,分界点之后的每次的变化都是固定的,设为 c n t cnt cnt。
其次,如果这时候已经满足 x > = m x>=m x>=m,就输出答案;不满足的话,就计算出每次增加 c n t cnt cnt,直至 x > = m x>=m x>=m的次数。
但是这样会出现一个问题,有可能在某次遍历的过程中, x > = m x>=m x>=m已经满足了,但是后面有负数,就导致了这次遍历完成后, x > = m x>=m x>=m又不满足了,这样就使得答案增大。
解决方案是提前记录前缀和的最大值,在计算每次增加 c n t cnt cnt增加多少次能够满足 x > = m x>=m x>=m的时候,提前将最大值减去。 - 代码:
const int maxn=1e5+7,maxm=210000;
ll n,m,a[maxn];
int main(){
int _=read;
while(_--){
n=read,m=read;
ll sum=0,maxx=-inf;
rep(i,1,n) a[i]=read,sum+=a[i],maxx=max(maxx,sum);
ll x=0,res=0,lasx=0,cnt=0,lascnt=-1;
while(1){
rep(i,1,n){
x=max(0ll,x+a[i]);
if(x>=m) break;
}
res++;
if(x>=m) break;
cnt=x-lasx;
if(lascnt==cnt) break;
lascnt=cnt,lasx=x;
}
if(x>=m) printf("%lld\n",res);
else{
if(cnt==0){
puts("-1");continue;
}
if(maxx==cnt){
ll t=m-x;
res+=t/cnt;
if(t%cnt) res++;
printf("%lld\n",res);
}
else{
ll t=m-x-maxx;
//cout<<t<<" "<<cnt<<" "<<res<<endl;
res+=t/cnt+1;
//cout<<t/cnt<<endl;
if(t%cnt) res++;
printf("%lld\n",res);
}
}
}
return 0;
}