1001 Mod, Or and Everything
签到题吧。直接暴力打表,前100个数就看出来了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve()
{
ll n;
scanf("%lld",&n);
if((n&(-n)) == n)
{
printf("%lld\n",max(0ll,n / 2 - 1));
return ;
}
while((n&(-n)) != n) n -= (n & (-n));
printf("%lld\n",n - 1);
return ;
}
int main()
{
ll t;
scanf("%lld",&t);
while(t -- )
{
solve();
}
return 0;
}
1005 Minimum spanning tree
比签到难一点点。2-n,只要是质数,就将它和2相乘,否则就是他本身,最后所有的加起来,预处理出所有的质数,线性筛法(好像也叫欧拉筛法,O(n)的吧,挺快的)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e7 + 10;
int isprime[N];
bool st[N];
int cnt;
void solve1() // 找出1-N中所有的质数
{
for(int i = 2;i < N;i ++ )
{
if(!st[i])
{
isprime[cnt ++] = i;
}
for(int j = 0;j < cnt && isprime[j] * i <= N;j ++ )
{
st[i * isprime[j]] = 1;
if(i % isprime[j] == 0) break;
}
}
}
void solve(int n)
{
ll res = 0;
res += (ll)(n + 3) * (n - 2) / 2;
for(int i = 1;i < cnt;i ++ )
{
if(isprime[i] <= n) res += isprime[i];
}
printf("%lld\n",res);
//for(int i = 0;i < cnt;i ++ ) cout << isprime[i] << " \n"[i == cnt - 1];
}
int main()
{
int t;
cin >> t;
solve1();
while(t -- )
{
int n;
scanf("%d",&n);
solve(n);
}
return 0;
}
1006 Xor sum
01字典树。为了做这道题,学了一天的字典树,还去做了基础的练习,感觉理解了就简单了好多。维护前缀异或和,然后把他们插入01字典树,访问到叶子节点的时候,这串二进制数就代表当前的前缀异或和。依次访问,开一个id[]记录每个点最右边在哪,最后处理一下就行了。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10, INF = 0x3f3f3f3f;
int tr[N*15][2],t,n,a[N],x,id[N*15],idx = 0,l,r,minn,k,len,b[N];
int res;
void insert( int pp , int x )
{
int p = 0;
for(int i=30;i>=0;i--)
{
if( !tr[p][ ((x>>i) & 1 )] )
{
idx++;
tr[p][ ((x>>i) & 1 )] = idx;
}
p = tr[p][ ((x>>i) & 1 )];
id[p] = pp;
}
}
int search( int x )
{
int p = 0;
for(int i=30;i>=0;i--)
{
int s = ((x>>i) & 1);
int kk = ((k >> i) & 1);
if(kk == 0)
{
if(tr[p][!s])
{
res = max(res,id[tr[p][!s]]);
}
p = tr[p][s];
}
else
{
p = tr[p][!s];
}
if(p == 0) return -1;
}
return id[p];
}
void solve( )
{
memset(tr,0,sizeof(tr));
memset(id,0,sizeof(id));
len = INF;
minn = INF;
res = -1;
l = -1,r = n+1;
idx = 0;
scanf("%d %d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&b[i]);
a[i] = b[i] ^ a[i-1];
}
insert( 0 , a[0] );
for(int i=1;i<=n;i++)
{
int j = search( a[i] );
int temp1 = b[i];
if(j != -1)
{
res = max(res,j);
}
j = res;
int ll = j+1,rr = i;
if( temp1>=k )
{
l = r = i;
break;
}
else if( j != -1 && (a[j] ^ a[i]) >= k )
{
if( (rr-ll+1) < len )
{
l = ll;
r = rr;
len = r-l+1;
}
else if( (rr-ll+1) == len )
{
if( ll<l )
{
l = ll;
r = rr;
len = r-l+1;
}
}
}
insert( i , a[i] );
}
if( l==-1 )
{
printf("-1\n");
}
else printf("%d %d\n",l,r);
}
int main()
{
//freopen("in.txt","r",stdin);
cin>>t;
while( t-- )
{
solve();
}
return 0;
}
1008 Maximal submatrix
直接转换成01矩阵,就是板子题了。可以用悬线法或者单调栈求最大01矩阵,其实这种方法还能求其他满足条件的子矩阵。可以看看这道题,似乎是19年银川的区域赛的题,学习的时候偶然发现的,就做了一下,挺简单的,也是先转换,然后用悬线法求,https://nanti.jisuanke.com/t/42391
#include<bits/stdc++.h>
using namespace std;
const int N = 2010;
int n,m;
int s[N][N],b[N][N];
int l[N],r[N],h[N];
int solve(int cur)
{
for(int i = 1;i <= m;i ++ )
{
if(cur == 1) h[i] = 1;
else
{
if(b[cur][i] == 1 && b[cur - 1][i] == 1) h[i] = 1 + h[i];
else if(b[cur][i] == 1 && b[cur - 1][i] == 0) h[i] = 2;
else h[i] = 0;
}
}
for(int i = 1;i <= m;i ++ )
{
l[i] = i;
while(l[i] > 1 && b[cur][i] == 1 && (b[cur][l[i] - 1] == 1) && (h[l[i] - 1] >= h[i])) l[i] = l[l[i] - 1];
}
for(int i = m;i >= 1;i -- )
{
r[i] = i;
while(r[i] < m && b[cur][i] == 1 && (b[cur][r[i] + 1] == 1) && (h[r[i] + 1] >= h[i])) r[i] = r[r[i] + 1];
}
int res = -1;
for(int i = 1;i <= m;i ++ )
{
res = max(res,(r[i] - l[i] + 1) * h[i]);
}
//for(int i = 1;i <= m;i ++ ) cout << l[i] << " " << r[i] << " "<< h[i] << " \n"[i == m];
return res;
}
int main()
{
int t;
cin >> t;
while(t -- )
{
scanf("%d %d",&n,&m);
for(int i = 1;i <= n;i ++ )
for(int j = 1;j <= m;j ++ )
{
if(i == 1) b[i][j] = 1;
scanf("%d",&s[i][j]);
}
for(int i = 2;i <= n;i ++ )
for(int j = 1;j <= m;j ++ )
if(s[i][j] >= s[i - 1][j]) b[i][j] = 1;
else b[i][j] = 0;
int res = m;
for(int i = 1;i <= n;i ++ ) res = max(res,solve(i));
printf("%d\n",res);
}
return 0;
}
1009 KD-Graph
感觉就是最小生成树,克鲁斯卡尔算法的感觉,定义为签到题。。。先对所有边排个序,每次取出最小的边,判断该边的两个点是否在一个集合,如果不在,则将他们放入一个集合(并查集实现),每次再判断一下当前的集合是不是等于题目要求的集合数量。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n,m,k;
struct node
{
int u,v,c;
}a[N];
int p[N];
bool cmp(node a,node b)
{
return a.c < b.c;
}
int fin(int x)
{
if(p[x] != x) p[x] = fin(p[x]);
return p[x];
}
void solve()
{
scanf("%d %d %d",&n,&m,&k);
for(int i = 1;i <= n;i ++ ) p[i] = i;
for(int i = 1;i <= m;i ++ )
{
int u,v,c;
scanf("%d %d %d",&u,&v,&c);
a[i] = {u,v,c};
}
sort(a + 1,a + m + 1,cmp);
int now = n;
int res = 0;
for(int i = 1;i <= m;i ++ )
{
if(a[i].c != a[i - 1].c)
{
if(now == k)
{
printf("%d\n",res);
return ;
}
}
if(fin(a[i].u) == fin(a[i].v)) continue;
now --;
p[fin(a[i].u)] = fin(a[i].v);
res = a[i].c;
}
printf("%d\n",now == k ? res : -1);
}
int main()
{
int t;
cin >> t;
while(t -- )
{
solve();
}
return 0;
}
1010 zoto
莫队加树状数组。之前莫队都不知道是啥,也是吐了花了一天搞理论,然后也做了几道基础题才把这道搞出来。莫队懂了原理还是很简单的,就是一个优化版的暴力,核心思想就是分块和移动,一般分块大小就是更号n,记得有一个什么奇偶分块,可以加快查找速度,后面再去补一下。用莫队处理m次询问,然后他要询问给定y轴区间内不同的点的个数,立马就想到了树状数组,可能是逆序对做多了的原因吧。莫队每次移动的时候要维护一下当前横轴区间每个数出现的次数,加入到树状数组里面去,就可以了。最后莫队的时间复杂度后面再去看看证明,感觉很神奇的样子。。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n,m;
int a[N];
int cnt[N];
int pos[N];
int tr[4 * N];
struct node
{
int l,r,up,down;
int id;
int res;
}query[N];
int lowbit(int x)
{
return x & (-x);
}
bool cmp1(node a,node b)
{
if(pos[a.l] != pos[b.l]) return a.l < b.l;
else return a.r < b.r;
}
bool cmp2(node a,node b)
{
return a.id < b.id;
}
void update(int x,int c)
{
for(int i = x;i <= n;i += lowbit(i)) tr[i] += c;
}
int sum(int x)
{
int ans = 0;
for(int i = x;i > 0;i -= lowbit(i)) ans += tr[i];
return ans;
}
void add(int i)
{
if(!cnt[a[i]]) update(a[i],1);
cnt[a[i]] ++;
}
void del(int i)
{
cnt[a[i]] --;
if(!cnt[a[i]]) update(a[i],-1);
}
void solve()
{
for(int i = 0;i < 4 * N;i ++ ) tr[i] = 0;
scanf("%d %d",&n,&m);
int block = sqrt(n);
for(int i = 1;i <= n;i ++ ) scanf("%d",&a[i]), a[i] ++, cnt[i] = 0,pos[i] = i / block;
for(int i = 1;i <= m;i ++ )
{
int x1,y1,x2,y2;
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
query[i].l = x1, query[i].r = x2, query[i].up = ++ y2,query[i].down = ++ y1;
query[i].id = i;
}
sort(query + 1,query + 1 + m,cmp1);
int l = 0, r = 0;
for(int i = 1;i <= m;i ++ )
{
int ql = query[i].l, qr = query[i].r;
while(l < ql) del(l ++);
while(l > ql) add(-- l);
while(r < qr) add(++ r);
while(r > qr) del(r --);
query[i].res = sum(query[i].up) - sum(query[i].down - 1);
//cout << sum(query[i].up) << " " << sum(query[i].down - 1) << endl;
}
sort(query + 1,query + 1 + m,cmp2);
for(int i = 1;i <= m;i ++ )
cout << query[i].res << endl;
}
int main()
{
int t;
cin >> t;
while(t -- )
{
solve();
}
return 0;
}
1007 Pass!
额,至于这道题我是真没看懂,递推公式还能勉强看懂,bsgs算法还没看懂,后面慢慢看吧,先把代码贴上。
#include<bits/stdc++.h>
using namespace std;
#define N 10000010
#define LL long long
#define MOD 998244353
LL T,n,x,base,B=sqrt(MOD);
unordered_map<LL,LL>mp;
LL qow(LL x,LL y){return y?(y&1?x*qow(x,y-1)%MOD:qow(x*x%MOD,y/2)):1;}
int main()
{
scanf("%lld",&T);
while(T--){
scanf("%lld%lld",&n,&x);
x=x*n%MOD*qow(n-1,MOD-2)%MOD;
mp.clear();
mp[1]=0;
base=(n-1)*(n-1)%MOD;
LL f1=(x+MOD-1)*(n-1)%MOD;
LL f2=(x+1)%MOD;
if(f1==1){
printf("0\n");
continue;
}
if(f2==1){
printf("1\n");
continue;
}
LL p=1,flg=0;
for(LL i=1;i<=B;i++){
p=p*base%MOD;
mp[p]=i;
if(f1==p){
printf("%lld\n",2ll*i);
flg=1;
break;
}
if(f2==p){
printf("%lld\n",2ll*i+1);
flg=1;
break;
}
}
if(flg)continue;
p=qow(p,MOD-2);
for(LL k=1,kp=p;k*B<MOD;k++,kp=kp*p%MOD){
if(mp.count(f1*kp%MOD)){
printf("%lld\n",2ll*(k*B+mp[f1*kp%MOD]));
flg=1;
break;
}
if(mp.count(f2*kp%MOD)){
printf("%lld\n",2ll*(k*B+mp[f2*kp%MOD])+1);
flg=1;
break;
}
}
if(flg)continue;
printf("-1\n");
}
}