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)\)
程式碼:
#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
*/