1. 程式人生 > 實用技巧 >2020.11.3校測 解題報告

2020.11.3校測 解題報告

2020.11.3校測解題報告

得分情況:

期望得分 實際得分
T1 位運算之謎 30 30
T2 遊戲 100 100
T3 或和異或 30 0
T4 連結 0 0

T1 位運算之謎

題目描述

\(Aliemo\) 有兩個數 \(a, b\) ,但是他想考考你,所以他想給你另外兩個數 \(x, y\)

\(a+b\) 的值為 \(x\) ,\(a\&b\) 的值為 \(y\),首先需要判斷能否有一組 \(a,b\) 滿足當前的情況,如果有,那麼求出\(a \ xor \ b\),否則輸出 \(-1\)(其中 \(a,b>0\)

思路

顯然結論 $$a+b=((a\And b)<< 1)+(a \oplus b )(\oplus 為異或)$$ 成立

所以只需判斷 \(a\And b\)\(a+b\) 的大小,根據上述結論可以求出 \(a\oplus b\)\(x-2\times y\) ,因為 \(\And\) 時取兩個相同的位,\(\oplus\) 時取的是不相同的位,所以 \(a\oplus b\)\(a\And b\) 這兩個數 \(\And\) 起來一定為零,可作為判斷依據。

程式碼

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#define ll long long
#define InF 0x7fffffff
#define Max 10e5
#define Min -10e5
using namespace std;
/*==========================================快讀*/
ll read()
{
    ll x=0,f=1;
	char c=getchar();
    while(c<'0'||c>'9')
	{
		if(c=='-') 
			f=-1;
		c=getchar();
	}
    while(c>='0'&&c<='9')
    { 
		x=(x<<3)+(x<<1)+(c^48); 
		c=getchar();
	}
    return x*f;
} 
/*======================================定義變數*/
int T;

ll x,y;
ll rem;
/*====================================自定義函式*/

/*========================================主程式*/
int main()
{
  	scanf("%d",&T);
  	while(T--)
  	{
  		x=read(),y=read();
		rem=x-2*y;
		if (rem<0||(rem&y)>0)
			printf("-1\n");
		else 
			printf("%I64d\n",rem);
	}
	return 0;
}

T2 遊戲

題目描述:

\(luckyblock\) 又開始和社團的萌妹子玩遊戲了。

在今天的遊戲中,\(luckyblock\) 將會得到一個 \(n\times m\) 且全為小寫字母的矩陣,他可以從矩陣中任選一塊正方形,但必須保證該正方形中任意一類小寫字母個數之和不能超過 $k $,換而言之,在該正方形中,‘a’字元個數不能超過 \(k\) ,‘b’字元個數不能超過 \(k\)\(\dots\),‘z’字元個數不能超過 \(k\)

\(luckyblock\) 現在想知道,以 \((i,j)\) 為左上角且符合以上要求的正方形中,邊長最大的是多少?

思路:

\(n^2\) 列舉每個點,然後對每個 \((i,j)\)

二分邊長,二維字首和輔助 \(check\)

程式碼:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#define ll long long
#define InF 0x7fffffff
#define Max 10e5
#define Min -10e5
using namespace std;
/*==========================================快讀*/
int read()
{
    int x=0,f=1;
	char c=getchar();
    while(c<'0'||c>'9')
	{
		if(c=='-') 
			f=-1;
		c=getchar();
	}
    while(c>='0'&&c<='9')
    { 
		x=(x<<3)+(x<<1)+(c^48); 
		c=getchar();
	}
    return x*f;
} 
/*======================================定義變數*/
int n,m,k;

char a;
int s[310][310][30];

int ans[310][310];
/*====================================自定義函式*/
void sum1(int n,int m)
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<26;k++)
				s[i][j][k]+=s[i-1][j][k];
}

void sum2(int n,int m)
{
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			for(int k=0;k<26;k++)
				s[i][j][k]+=s[i][j-1][k];
}

void summ(int n,int m)
{
	sum1(n,m);
	sum2(n,m);
}

bool check(int x,int y,int len)
{
	int x_=x+len-1,y_=y+len-1;
	for(int i=0;i<26;i++)
		if((s[x_][y_][i]-s[x-1][y_][i]-s[x_][y-1][i]+s[x-1][y-1][i])>k)
			return false;
	return true;
}

/*========================================主程式*/
int main()
{
//	freopen("game.in","r",stdin);
//	freopen("game.out","w",stdout);
  	n=read();
  	m=read();
  	k=read();
  	for(int i=1;i<=n;i++)
  		for(int j=1;j<=m;j++)
  		{
  			cin>>a;
			s[i][j][a-'a']++;		
		}
	summ(n,m);
//	for(int i=1;i<=n;i++)
//	{
//		for(int j=1;j<=m;j++)
//			
//				printf("%d ",s[i][j][1]);
//		printf("\n");
//	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			int l=1;
			int r= (n-i+1)>(m-j+1) ? (m-j+1) : (n-i+1);
			while(l<=r)
			{
				int mid=(l+r)>>1;
				if(check(i,j,mid))
				{	
					ans[i][j]=mid;
					l=mid+1;
				}
				else 
					r=mid-1;
//				printf("%d ",mid);
//				printf("\n");
			}
//			printf("%d ",a);
//			printf("\n");
//			ans[i][j]=a;
			printf("%d ",ans[i][j]);
		}
		printf("\n");
	}
	return 0;
}

/*

3 3 2
aaa
bcd
efg

*/

T3 或和異或

思路:

線段樹!!!

想象成一顆線段樹。最底層就是第一層操作,依次向上是第 \(2,3,4,5\cdots\) 次操作。我們可以線上段樹上記錄一個 $ dep$ , 可以發現一個很明顯的東西就是與最底層深度相同的他合併的 時候就是 \(or\) 否則就是 \(xor\)

程式碼:

//by dead_gun 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
#include<stack>
#define ll long long
#define InF 0x7fffffff
#define Max 10e5
#define Min -10e5
#define lson rt << 1
#define rson rt << 1 | 1
#define N 200010
#define M 1010
using namespace std;
/*==========================================快讀*/
int read()
{
    int x=0,f=1;
	char c=getchar();
    while(c<'0'||c>'9')
	{
		if(c=='-') 
			f=-1;
		c=getchar();
	}
    while(c>='0'&&c<='9')
    { 
		x=(x<<3)+(x<<1)+(c^48); 
		c=getchar();
	}
    return x*f;
} 
/*======================================定義變數*/
int n, m;
struct node{
	int sum, len;
}tree[N << 2];
/*====================================自定義函式*/
void update(int rt, int val, int l, int r, int pow)
{
	if (l == r)
	{
		tree[rt].sum = val;
		return;
	}
	int mid = (l + r) >> 1;
	if (pow <= mid) update(lson, val, l, mid, pow);
	else update(rson, val, mid + 1, r, pow);
	push_up(rt);
}

void build(int rt, int l, int r)
{
	tree[rt].len = r - l + 1;
	if (l == r)
	{
		tree[rt].sum = read();
		return;
	}
	int mid = (l + r) >> 1;
	build(lson, l, mid);
	build(rson, mid + 1, r);
	push_up(rt);
}

void push_up(int rt)
{
	double  x = log(tree[rt].len) / log(2);
	int x1 = x;
	if (x1 % 2 == 1) 
		tree[rt].sum = (tree[lson].sum | tree[rson].sum);
	else 
		tree[rt].sum = (tree[lson].sum ^ tree[rson].sum);
}


/*========================================主程式*/
int main()
{
//	freopen("xor.in", "r", stdin);
//	freopen("xor.out", "w", stdout);
	n = read(), m = read();
	n = (1 << n);
	build(1, 1, n);
	for (int i = 1, x, y; i <= m; i++)
	{
		x = read(), y = read();
		update(1, y, 1, n, x);
		printf("%d\n", tree[1].sum);
	}
}

指標線段樹版:

//by dead_gun
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define int long long
#define INF (1e13 + 7)
#define MANX MAXN
#define MAXN 2000000

using namespace std;

inline int read()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();}
	return f * x;
}

int n, q, a[MAXN], cnt;

struct node{
	int l, r, w, tag;
	node *left, *right;
}pool[MAXN];

inline void build(int l, int r, node *cur)
{
	cur->l = l;
	cur->r = r;
	if (l == r)
	{
		cur->w = a[l], cur->tag = 0;
		return;
	}
	node *lson = &pool[++cnt];
	node *rson = &pool[++cnt];
	cur->left = lson;
	cur->right = rson;
	int mid = (l + r) >> 1;
	build(l, mid, lson);
	build(mid + 1, r, rson);
	if (cur->left->tag == 0) cur->w = cur->left->w | cur->right->w, cur->tag = 1;
	else cur->w = cur->left->w ^ cur->right->w, cur->tag = 0;
}

inline void change(int x, int w, node *cur)
{
	if (cur->l == x && cur->r == x)
	{
		cur->w = w;
		return;
	}
	int mid = (cur->l + cur->r) >> 1;
	if (x <= mid) change(x, w, cur->left);
	if (x > mid) change(x, w, cur->right);
	if (cur->left->tag == 0) cur->w = cur->left->w | cur->right->w;
	else cur->w = cur->left->w ^ cur->right->w;
}

signed main()
{
	freopen("xor.in", "r", stdin);
	freopen("xor.out", "w", stdout);
	int n = read(), q = read();
	for (int i = 1; i <= (1 << n); i++) a[i] = read();
	build(1, (1 << n), pool);
	while (q--)
	{
		int x = read(), y = read();
		change(x, y, pool);
		cout << pool->w;
		puts("");
	}
	return 0;
}

T4 連結

思路:

我們可以知道 $$2^i=\sum^{i-1}_{j=0}{2^j}+1$$ ,所以第 \(i\) 條邊一定是大於前邊所有邊的和的,我們可以根據這個

跑出一棵生成樹來,他的最短路徑顯然在這個生成樹上,所以我們可以在這個樹上做樹形 \(DP\)

有一種很暴力的做法就是,列舉每個結點,然後以這個結點為根找出每個點和他的路徑長度,最後加和。

我們在這裡考慮 \(x,to\) 這兩個節點的答案應該是什麼,假如我們已經得到了 \(x\) 點的答案,考慮如何得 到 \(to\) 點的答案,我們從 \(x\) 點轉移到 \(to\) 點的時候,\(to\) 以及 \(to\) 子樹內的點到 \(to\) 的距離是 到 \(x\) 距離減去這條邊的權值。 然後從 \(x\) 轉移到 \(to\) 點,可以看出除了 \(to\) 及其子樹中的點,邊權都增加了這條邊的邊權,所以 $$ans_{to}=ans_x - siz_{to} \times dis + (siz_x-siz_to)\times dis(dis 為連結 x 與 to 的邊權)$$ 。

程式碼:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#define int long long
#define INF (1e13 + 7)
#define MANX MAXN
#define MAXN 200000
#define MOD 1000000007

using namespace std;

inline int read()
{
	int x = 0, f = 1; char c = getchar();
	while (c > '9' || c < '0') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + (c ^ 48); c = getchar();}
	return f * x;
}

int n, m, a[MAXN], fa[MAXN];

inline int find(int x){return fa[x] == x ? x : fa[x] = find(fa[x]);}
inline int qpow(int a, int b)
{
	int sum = 1;
	while (b)
	{
		if (b & 1) sum *= a, sum %= MOD;
		a *= a, a %= MOD, b >>= 1;
	}
	return sum;
}

struct node{
	int u, v, w, next;
}edge[MAXN];

int Num, head[MAXN];

inline void build(int u, int v, int w)
{
	Num++;
	edge[Num].u = u;
	edge[Num].v = v;
	edge[Num].w = w;
	edge[Num].next = head[u];
	head[u] = Num;
}

int size[MAXN], w[MAXN], sum, f[MAXN];

void dfs(int u, int fa)
{
	for (int i = head[u]; i; i = edge[i].next)
	{
		int v = edge[i].v;
		if (v == fa) continue;
		w[v] = w[u] + edge[i].w;
		w[v] %= MOD;
		dfs(v, u);
		size[u] += size[v];
	}
}

void dfs1(int u, int fa)
{
	for (int i = head[u]; i; i = edge[i].next)
	{
		int v = edge[i].v;
		if (v == fa) continue;
		int add = ((sum - size[v]) % MOD + MOD) % MOD;
		int del = size[v] * edge[i].w;
		add *= edge[i].w;
		add %= MOD;
		f[v] = ((f[u] + add - del) % MOD + MOD) % MOD;
		dfs1(v, u);
	}
}

signed main()
{
	freopen("link.in", "r", stdin);
	freopen("link.out", "w", stdout);
	n = read(), m = read();
	for (int i = 1; i <= n; i++) a[i] = read(), fa[i] = i;
	int flag = (a[1] == 1);
	for (int i = 1; i <= n; i++)
		a[i] ^= flag, size[i] = a[i];
	// for (int i = 1; i <= n; i++)
	// 	cout << a[i] << ' ';
	for (int i = 1; i <= m; i++)
	{
		int u = read(), v = read();
		int fu = find(u), fv = find(v);
		if (fu == fv) continue;
		fa[fu] = fv;
		build(u, v, qpow(2, i));
		build(v, u, qpow(2, i));
	}
	dfs(1, 0);
	sum = size[1];
	for (int i = 1; i <= n; i++)
		if (a[i])
			f[1] += w[i], f[1] %= MOD;
	dfs1(1, 0);
	int ans = 0;
	for (int i = 1; i <= n; i++)
		if (!a[i])
			ans += f[i], ans %= MOD;
	cout << ans % MOD;
	return 0;
}
/*
3 2
1 0 1
1 2
2 3

4 3
1 0 0 1
2 3
2 4
2 1
*/