1001. Mod, Or and Everything
题意:
给你一个,求对于任意
,
按位或的和。
显然对于,
可以取到
的所有值,对于
,
一定小于
,所以答案一定为2的次幂 - 1,打表找一下具体是多少次幂即可
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
#define debug(x) cout << #x << ":" << (x) << "\n";
using namespace std;
typedef long long ll;
const int maxn = 1e3 + 10;
const int maxm = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const ll llinf = 0x3f3f3f3f3f3f3f;
ll n;
int main()
{
int _;
// int a = 0;
scanf("%d", &_);
while ( _-- ) {
scanf("%lld", &n);
ll temp = 1;
while ( temp * 2 < n ) {
temp *= 2;
}
printf("%lld\n", temp - 1);
}
return 0;
}
1003. Puzzle loop
题意:
在方格中选择若干条边,使得它们形成若干个没有公共边的环,并且满足方格中数字的限制。
由于是若干个没有公共边的环,那么对于方格中的任意一点,都有与它相连的边中被选中的边数为偶数。我们将边被选中记为1,未选中记为0,则对于每一个点和每一个方格都可以列出一个异或方程,用高斯消元解这个异或方程组,若自由元个数为k,则答案为。复杂度
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
#include <bitset>
#define debug(x) cout << #x << ":" << (x) << "\n";
using namespace std;
typedef long long ll;
const int maxn = 2e3 + 10;
const int maxm = 1e5 + 10;
const int inf = 0x3f3f3f3f;
const ll llinf = 0x3f3f3f3f3f3f3f;
const int p = 998244353;
bitset<maxn> a[maxn];
int n, m;
int b[40][40];
int id[40][40];
int dx[] = { 1,0,-1,0 };
int dy[] = { 0,1,0,-1 };
int cnt = 0;
int ans = 0;
char ch[maxn];
void guass()
{
int now = 1;
for ( int i = 1;i <= id[0][0];i++ ) {
for ( int j = now;j <= cnt;j++ ) {
if ( a[j][i] ) {
swap(a[j], a[now]);
break;
}
}
if ( !a[now][i] )
continue;
for ( int j = 1;j <= cnt;j++ ) {
if ( j == now )
continue;
if ( a[j][i] )
a[j] ^= a[now];
}
now++;
}
for ( int i = now;i <= cnt;i++ ) {
if ( a[i][id[0][0] + 1] ) {
ans = -1;
return;
}
}
ans = id[0][0] - now + 1;
}
long long ksm(long long x, long long y)
{
long long ret = 1;
do {
if ( y & 1 )
ret = ret * x % p;
x = x * x % p;
} while ( y >>= 1 );
return ret;
}
int main()
{
// freopen("D:\\Codefield\\CPP\\HDU\\MINIEYE1\\in.txt", "r", stdin);
// freopen("D:\\Codefield\\CPP\\HDU\\MINIEYE1\\out.txt", "w", stdout);
int _;
scanf("%d", &_);
while ( _-- ) {
scanf("%d%d", &n, &m);
memset(b, 0, sizeof b);
memset(id, 0, sizeof id);
id[0][0] = 0;
ans = 0;
cnt = 0;
int tt1 = 2 * n - 1;
int tt2 = 2 * m - 1;
for ( int i = 1;i <= n - 1;i++ ) {
scanf("%s", ch + 1);
for ( int j = 1;j <= m - 1;j++ ) {
if ( ch[j] == '.' )
b[2 * i][2 * j] = -1;
else b[2 * i][2 * j] = ch[j] - '0';
// scanf("%d", &b[2 * i][2 * j]);
}
}
for ( int i = 1;i <= tt1;i++ ) {
for ( int j = 1;j <= tt2;j++ ) {
if ( i % 2 == 1 && j % 2 == 1 )
continue;
if ( i % 2 == 0 && j % 2 == 0 )
continue;
id[i][j] = ++id[0][0];
}
}
for ( int i = 1;i <= tt1;i++ ) {
for ( int j = 1;j <= tt2;j++ ) {
if ( i % 2 == 1 && j % 2 == 0 )
continue;
if ( i % 2 == 0 && j % 2 == 1 )
continue;
if ( b[i][j] == -1 )
continue;
cnt++;
a[cnt][id[0][0] + 1] = b[i][j];
for ( int k = 0;k <= 3;k++ ) {
int x = i + dx[k];
int y = j + dy[k];
if ( x >= 1 && x <= tt1 && y >= 1 && y <= tt2 ) {
a[cnt][id[x][y]] = 1;
}
}
}
}
// for ( int i = 1;i <= cnt;i++ ) {
// for ( int j = 1;j <= id[0][0] + 1;j++ ) {
// // printf("%d ", a[i][j]);
// cout << a[i][j] << " ";
// }
// printf("\n");
// }
// printf("\n\n\n");
guass();
if ( ans == -1 ) {
printf("0\n");
}
else
printf("%lld\n", ksm(2ll, (long long)(ans)));
for ( int i = 1;i <= cnt;i++ )
a[i].reset();
}
return 0;
}
1005. Minimum spanning tree
题意:
给定一张无向完全图,点与
之间的边权为
,求最小生成树
显然对于质数,我们选择它与2连的那条边,权值为
,对于合数
,我们选择它与它的一个约数的相连的边,权值为
,
,用欧拉筛预处理质数即可,复杂度
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
using namespace std;
const int maxn = 10000010;
int vis[maxn], prime[maxn];
void eulersieve(int x)
{
int cnt = 0;
for (int i = 2;i <= x;i++) {
if (!vis[i]) {
vis[i] = i;
prime[++cnt] = i;
}
for (int j = 1;j <= cnt;j++) {
if (i * prime[j] > x) {
break;
}
vis[i * prime[j]] = prime[j];
if (i % prime[j] == 0)
break;
}
}
}
long long ans[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
eulersieve(10000000);
int t, n, i;
ans[2] = 0;
for(i = 3; i <= 10000000; i++)
{
long long tmp = i;
if(vis[i] == i) tmp *= 2;
ans[i] = ans[i - 1] + tmp;
}
cin >> t;
while(t--){
cin >> n;
cout << ans[n] << endl;
}
return 0;
}
1006. Xor sum
题意:
给定一个序列a,和一个询问k,求满足异或和不小于k的最短连续子段。
首先预处理出前缀异或和,逐个
插入进01trie中,每次在01trie上进行查询,并更新答案。复杂度
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <ctime>
using namespace std;
const int maxn = 1e5 + 10;
int n, k;
int a[maxn];
int trie[maxn * 30][2];
int exist[maxn * 30];
int cnt;
int s[40];
int ansl, ansr;
int read()
{
int x = 0, f = 1;
char ch;
while ( (ch = getchar()) < '0' || ch > '9' )
if ( ch == '-' )
f = -1;
while ( ch >= '0' && ch <= '9' ) {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void insert(int* s, int num)
{
int p = 0;
for ( int i = 30;i >= 1;i-- ) {
if ( !trie[p][s[i]] ) {
trie[p][s[i]] = ++cnt;
p = trie[p][s[i]];
}
else
p = trie[p][s[i]];
exist[p] = num;
}
}
int find(int* s)
{
int p = 0;
int ret = -1;
for ( int i = 30;i >= 1;i-- ) {
if ( ((k >> (i - 1)) & 1) == 0 ) {
if ( trie[p][s[i] ^ 1] )
ret = max(ret, exist[trie[p][s[i] ^ 1]]);
p = trie[p][s[i]];
}
else p = trie[p][s[i] ^ 1];
if ( !p )
return ret;
}
return max(ret, exist[p]);
}
void clear(int p = 0)
{
if ( trie[p][0] )
clear(trie[p][0]);
if ( trie[p][1] )
clear(trie[p][1]);
trie[p][0] = trie[p][1] = 0;
// exist[p] = 0;
}
int main()
{
// freopen("D:\\Codefield\\CPP\\HDU\\MINIEYE1\\in.txt", "r", stdin);
// freopen("D:\\Codefield\\CPP\\HDU\\MINIEYE1\\out2.txt", "w", stdout);
int _ = read();
// scanf("%d", &_);
while ( _-- ) {
cnt = 0;
// scanf("%d%d", &n, &k);
n = read();
k = read();
clear();
a[0] = 0;
for ( int i = 1;i <= n;i++ ) {
// scanf("%d", &a[i]);
a[i] = read();
a[i] = a[i] ^ a[i - 1];
}
ansl = 0, ansr = n + 1;
memset(s, 0, sizeof s);
insert(s, 0);
for ( int i = 1;i <= n;i++ ) {
memset(s, 0, sizeof s);
int temp = a[i];
int num = 0;
while ( temp ) {
s[++num] = temp & 1;
temp >>= 1;
}
int x = find(s);
if ( x != -1 && ansr - ansl + 1 > i - x ) {
ansr = i;
ansl = x + 1;
}
insert(s, i);
}
if ( ansl ) {
printf("%d %d\n", ansl, ansr);
}
else printf("-1\n");
}
return 0;
}
1007. Pass!
题意:
n个人进行传球,每个人可以传给其他任意一个人,求最少进行多少轮后可以球传回到1号的方案数为
首先很容易写出递推式,
由于n为定值,便可以转化为广义斐波那契数列求通项,解得
即
转化为离散对数的求解,分别讨论t的奇偶性,跑两次BSGS求解即可,复杂度
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int M=1e5+100;
const int mod=998244353;
inline int read(){
int x=0,k=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') k=-1;ch=getchar();}
while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
return x*k;
}
inline int GCD(int a,int b){return b?GCD(b,a%b):a;}
inline void write(int x){
if(x<0) x=-x,putchar('-');
if(x>9) write(x/10);
putchar(x%10+'0');
}
int x,y,n,m,inv,T;
inline int ksm(int a,int b,int mo)
{
int res=1;
a%=mo;
while(b)
{
if(b&1) res=(res*a)%mo;
a=a*a%mo;
b>>=1;
}
return res%mo;
}
inline int BSGS(int a,int b,int p)
{
if(b==1) return 0;
int m=ceil(sqrt(p));
int t=b;
unordered_map<int,int>dis;
dis[b]=0;
for(int j=1;j<m;j++)
{
t=t*a%p;
dis[t]=j;
}
int mi=ksm(a,m,p);
t=1;
for(int i=1;i<=m;i++)
{
t=t*mi%p;
if(dis.count(t)!=0) return (i*m-dis[t]);
}
return -1;
}
signed main()
{
cin>>T;
while(T--)
{
cin>>n>>x;
int A=n-1;
int x1=(x*n+n-1+mod)%mod;
int t1=BSGS(A,x1,mod); //奇数
// printf("t1的值为:%lld\n",t1);
if(t1%2==0&&t1!=-1) t1=-1;
int x2=(x*n-n+1+mod)%mod;
int t2=BSGS(A,x2,mod); //偶数
// printf("t2的值为:%lld\n",t2);
if(t2%2==1&&t2!=-1) t2=-1;
if(t1==-1&&t2==-1)
{
printf("-1\n");
continue;
}
if(t1==-1&&t2!=-1)
{
printf("%lld\n",t2);
continue;
}
if(t1!=-1&&t2==-1)
{
printf("%lld\n",t1);
continue;
}
printf("%lld\n",min(t1,t2));
}
return 0;
}
1008. Maximal submatrix
题意:
给定一个矩阵,找到最大子矩阵,满足每一列的数字单调不减。
建一个新的矩阵,处理出所有的位置,跑一个最大子矩阵即可。
#include <bits/stdc++.h>
#define x first
#define y second
#define pb push_back
using namespace std;
const int maxn = 2010;
int a[maxn][maxn], pre[maxn][maxn], l[maxn][maxn], r[maxn][maxn], q[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, t, i, j, top;
cin >> t;
//srand(time(NULL));
while (t--) {
cin >> n >> m;
for (i = 1; i <= n; i++)
for (j = 1; j <= m; j++) cin >> a[i][j];
for (i = 1; i <= m; i++) {
pre[1][i] = 1;
for (j = 2; j <= n; j++) {
if (a[j][i] < a[j - 1][i]) pre[j][i] = j;
else pre[j][i] = pre[j - 1][i];
}
}
for (i = 1; i <= n; i++)
{
top = 0;
for (j = 1; j <= m; j++) {
while (top && pre[i][j] > pre[i][q[top]]) {
r[i][q[top]] = j - 1;
--top;
}
q[++top] = j;
}
while (top) {
r[i][q[top]] = m;
--top;
}
}
for (i = 1; i <= n; i++) {
top = 0;
for (j = m; j; j--) {
while (top && pre[i][j] > pre[i][q[top]]) {
l[i][q[top]] = j + 1;
--top;
}
q[++top] = j;
}
while (top) {
l[i][q[top]] = 1;
--top;
}
}
int ans = m;
for (i = 1; i <= n; i++) {
for (j = 1; j <= m; j++) {
ans = max(ans, (r[i][j] - l[i][j] + 1) * (i - pre[i][j] + 1));
}
}
cout << ans << endl;
}
return 0;
}
1009. KD-Graph
题意:
给定一张图,求一个最小生成森林,使得森林中正好有K个连通块。如果加入了权值为w的边,则所有权值的边都必须被加入。
将边权从小到大排序,二分加入的最大边,判断是否可行即可。
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <string>
#define debug(x) cout << #x << ":" << (x) << "\n";
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
const int maxm = 5e5 + 10;
const int inf = 0x3f3f3f3f;
const ll llinf = 0x3f3f3f3f3f3f3f;
struct Edge {
int val, to, next;
}e[maxm << 1];
struct EDGE {
int u, v, w;
bool operator<(const EDGE& x)const
{
return w < x.w;
}
}E[maxn];
int fa[maxn];
int head[maxn];
int tot;
int num;
int n, m, k;
int find(int x)
{
if ( fa[x] != x )
fa[x] = find(fa[x]);
return fa[x];
}
void merge(int x, int y)
{
x = find(x);
y = find(y);
if ( x == y )
return;
fa[x] = fa[y];
num--;
}
void addedge(int u, int v, int w)
{
e[++tot] = Edge({ w,v,head[u] });
head[u] = tot;
}
int check(int x)
{
for ( int i = 1;i <= n;i++ )
fa[i] = i;
num = n;
for ( int i = 1;i <= m;i++ ) {
if ( E[i].w > x) {
break;
}
merge(E[i].u, E[i].v);
}
if ( num > k ) {
return 1;
}
else if ( num == k )
return 0;
else return -1;
}
int main()
{
int _;
int a = 0;
scanf("%d", &_);
int u, v, w;
while ( _-- ) {
scanf("%d%d%d", &n, &m, &k);
int l = 0, r = 0;
for ( int i = 1;i <= m;i++ ) {
scanf("%d%d%d", &E[i].u, &E[i].v, &E[i].w);
r = max(r, E[i].w);
// addedge(u, v, w);
// addedge(v, u, w);
}
sort(E + 1, E + m + 1);
int ans = -1;
while ( l < r ) {
int mid = (l + r) >> 1;
if ( check(mid) == 1 ) {
l = mid + 1;
}
else if( check(mid) == 0 ){
ans = mid;
r = mid;
}
else {
r = mid;
}
}
printf("%d\n", ans);
}
return 0;
}
1010. zoto
题意:
给定一系列点,m次询问,每次询问横坐标在
中有多少个在
的纵坐标。
显然需要用到莫队,但普通的莫队无法查询在某个区间内不同权值的数量,单次暴力查询的复杂度为无法接受。我们发现使用权值线段树可以实现单次
的查询复杂度,但莫队每次修改的复杂度却由
变为
,总的修改复杂度变为
,也难以接受。我们需要找到一个
修改并且查询复杂度可接受的数据结构。容易想到对值域进行分块,便可以做到
修改,单次 查询。总的复杂度为
。
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 10;
int st[maxn], ed[maxn], bel[maxn];
int n, m;
struct QQ {
int x0, x1, y0, y1;
int id;
bool operator<(const QQ& x)const
{
if ( bel[x0] != bel[x.x0] ) {
return x0 < x.x0;
}
return bel[x1] < bel[x.x1];
}
}q[maxn];
int num[maxn];
int cnt[maxn];
int f[maxn];
int ans[maxn];
void pre()
{
int sqr = sqrt(maxn - 10);
for ( int i = 1;i <= sqr;i++ ) {
st[i] = (i - 1) * sqr + 1;
ed[i] = i * sqr;
}
st[1] = 0;
ed[sqr] = n;
for ( int i = 1;i <= sqr;i++ ) {
for ( int j = st[i];j <= ed[i];j++ ) {
bel[j] = i;
}
}
}
void add(int x)
{
if ( !num[f[x]] ) {
cnt[bel[f[x]]]++;
}
num[f[x]]++;
}
void del(int x)
{
if ( num[f[x]] == 1 ) {
cnt[bel[f[x]]]--;
}
num[f[x]]--;
}
int query(int l, int r)
{
int ret = 0;
if ( bel[l] == bel[r] ) {
for ( int i = l;i <= r;i++ )
if ( num[i] )
ret++;
return ret;
}
for ( int i = l;i <= ed[bel[l]];i++ ) {
if ( num[i] )
ret++;
}
for ( int i = st[bel[r]];i <= r;i++ )
if ( num[i] )
ret++;
for ( int i = bel[l] + 1;i <= bel[r] - 1;i++ ) {
ret += cnt[i];
}
return ret;
}
int main()
{
int _;
scanf("%d", &_);
pre();
while ( _-- ) {
scanf("%d%d", &n, &m);
memset(num, 0, sizeof num);
memset(cnt, 0, sizeof cnt);
for ( int i = 1;i <= n;i++ ) {
scanf("%d", &f[i]);
}
for ( int i = 1;i <= m;i++ ) {
scanf("%d%d%d%d", &q[i].x0, &q[i].y0, &q[i].x1, &q[i].y1);
q[i].id = i;
}
sort(q + 1, q + m + 1);
int l = 1, r = 1;
add(l);
for ( int i = 1;i <= m;i++ ) {
int L = q[i].x0, R = q[i].x1;
while ( l > L ) {
add(--l);
}
while ( r < R ) {
add(++r);
}
while ( l < L ) {
del(l++);
}
while ( r > R ) {
del(r--);
}
ans[q[i].id] = query(q[i].y0, q[i].y1);
}
for ( int i = 1;i <= m;i++ ) {
printf("%d\n", ans[i]);
}
}
return 0;
}