A题
小学抽屉问题,
答案就是\(max(n - s,n - t) + 1\)
复杂度\(O(T)\)
代码:
#include
int T, n, m, i, j, k;
int a[2];
int main() {
scanf("%d",&T);
while(T--) {
scanf("%d %d %d",&n,&a[0],&a[1]);
printf("%d\n",std::max(n - a[0],n - a[1]) + 1);
}
}
B题
我们维护一个\(vector\)数组\(v\),其中\(v_i\)存的是字母\(i\)所出现的位置。
那么因为我们是依次\(\text{push_back}\)的,
那么第\(i\)个字母的位置其实就是\(v_{i - 1}\)(因为下标从0开始)。
我们查的时候只需要将\(t_i\)遍历一遍然后取个\(max\)就好。
而访问一个下标又是\(O(1)\)的操作,
所以总复杂度为\(O(m * max(|t_i|)\)
代码:
#include
const int maxn = 2e5 + 10;
const int maxm = 5e4 + 10;
inline void cmax(int& x,int y) {
if(x < y)
x = y;
}
char s[maxn];
int n, m, i, j, k;
int cnt[27];
std::vector v[27];
int main() {
int n; scanf("%d",&n);
scanf("%s",s + 1);
int l = strlen(s + 1);
for(int i = 1;i <= l;i++) {
int c = s[i] - 'a' + 1;
v[c].push_back(i);
}
scanf("%d",&m);
while(m--) {
static char t[maxn];
scanf("%s",t + 1);
int l = strlen(t + 1);
memset(cnt,0,sizeof(cnt));
for(int i = 1;i <= l;i++)
cnt[ t[i] - 'a' + 1 ]++;
int ans = 0;
for(int i = 1;i <= l;i++) {
int c = t[i] - 'a' + 1;
cmax(ans,v[c][ cnt[c] - 1 ]);
}
printf("%d\n",ans);
}
return 0;
}
C题
我们把所有操作全部分离,
然后排个序。
然后先处理不下降的,
赋值的时候如果左端点没有赋值过那就去\(n - x\)(一定保证小于左边一个),
否则就使用上一次的赋值(排过序,所以一定不降)。
剩下的如果有空隙那就表示一定是下降序列,
那么直接填就好。
于是这道题就做完了。
复杂度\(O(n)\)
#include
const int maxn = 1010;
const int maxm = 1010;
const int inf = 0x3f3f3f3f;
template inline void read(Tp& res) {
res = 0; char ch = getchar(); bool neg = 0;
while(!isdigit(ch))
neg |= ch == '-', ch = getchar();
while(isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if(neg)
res = -res;
}
inline void cmin(int& x,int y) {
if(x > y)
x = y;
}
inline int _abs(int x) {
return x < 0 ? -x : x;
}
int n, m, i, j, k;
int a[maxn];
std::vector > q1, q2;
int main() {
read(n); read(m);
for(int i = 1, op, l, r;i <= m;i++) {
read(op); read(l); read(r);
if(op)
q1.push_back(std::make_pair(l,r));
else
q2.push_back(std::make_pair(l,r));
}
std::sort(q1.begin(),q1.end());
std::sort(q2.begin(),q2.end());
int v;
for(auto i : q1) {
int l = i.first, r = i.second;
if(!a[l])
v = n - l;
for(int j = l;j <= r;j++)
a[j] = v;
}
for(int i = 1;i <= n;i++)
if(!a[i])
a[i] = n - i;
for(auto i : q2) {
int l = i.first, r = i.second, tmp = a[l];
bool ok = 0;
for(int j = l;j <= r;j++)
if(a[j] < tmp) {
ok = 1;
break;
}
if(!ok)
return puts("NO"), 0;
}
puts("YES");
for(int i = 1;i <= n;i++)
printf("%d ",a[i] + 5010);
return 0;
}
D题
又是一道典型的恶意评分。
我们思考一下冒泡排序的原理,每次只对两个数进行操作可以有同样的效果。
这时可以发现一点,对于一个\(a_i\),它一定要比前面的都小才可能到达指定的位置上去。
那么我们用线段树维护一下最小值就结束了...
结束了?对,结束了...
复杂度\(O(n log n)\)
代码:
#include
const int maxn = 3e5 + 10;
const int inf = 0x3f3f3f3f;
template inline void read(Tp& res) {
res = 0; char ch = getchar(); bool neg = 0;
while(!isdigit(ch))
neg |= ch == '-', ch = getchar();
while(isdigit(ch))
res = (res << 1) + (res << 3) + (ch & 15), ch = getchar();
if(neg)
res = -res;
}
inline int _min(int a,int b) { return a < b ? a : b; }
inline void cmin(int& a,int b) {
if(a > b)
a = b;
}
inline void cmax(int& a,int b) {
if(a < b)
a = b;
}
int n, m, i, j, k, T, mx;
int a[maxn], b[maxn], c[maxn], pos[maxn], t[maxn << 2];
std::queue q[maxn];
inline bool check() {
for(int i = 1;i <= mx;i++)
if(c[i] != 0)
return 0;
return 1;
}
inline void push_up(int u) {
t[u] = _min(t[u << 1],t[u << 1 | 1]);
}
void build(int l,int r,int u) {
if(l == r)
return t[u] = a[l], void();
int mid = (l + r) >> 1;
build(l,mid,u << 1);
build(mid + 1,r,u << 1 | 1);
push_up(u);
}
int query(int ql,int qr,int l,int r,int u) {
if(ql <= l && r <= qr)
return t[u];
int mid = (l + r) >> 1, res = inf;
if(ql <= mid)
cmin(res,query(ql,qr,l,mid,u << 1));
if(mid < qr)
cmin(res,query(ql,qr,mid + 1,r,u << 1 | 1));
return res;
}
void modify(int m,int l,int r,int u,int v) {
if(l == m && r == m)
return t[u] = v, void();
int mid = (l + r) >> 1;
if(m <= mid)
modify(m,l,mid,u << 1,v);
else
modify(m,mid + 1,r,u << 1 | 1,v);
push_up(u);
}
int main() {
read(T);
while(T--) {
read(n); mx = 0;
for(int i = 1;i <= n;i++)
c[i] = 0;
for(int i = 1;i <= n;i++) {
read(a[i]);
cmax(mx,a[i]);
c[ a[i] ]++;
}
for(int i = 1;i <= n;i++)
read(b[i]), cmax(mx,b[i]), c[ b[i] ]--;
if(!check()) {
puts("NO");
continue;
}
for(int i = 1;i <= n;i++)
q[ a[i] ].push(i);
for(int i = 1;i <= n;i++) {
pos[i] = q[ b[i] ].front();
q[ b[i] ].pop();
}
build(1,n,1); bool flag = 1;
for(int i = 1;i <= n;i++) {
int p = pos[i];
if(query(1,p,1,n,1) != b[i]) {
flag = 0;
puts("NO");
break;
}
modify(p,1,n,1,inf);
}
if(flag)
puts("YES");
}
return 0;
}
E题
树形dp。
设u为当前节点,
则\(f[fa_u] = S_{fa_u} + sz_{u} + n + S_{ch_u}\)
\(f[u] = S_{ch_u} + 2 * n - sz_u + S_{fa_u}\)
可以推出\(f[u] = f[fa_u] + n - sz_u * 2\)
转移即可,
#include
typedef long long ll;
const int maxn = 2e5 + 10;
int cnte, n, m, i, j, k;
int sz[maxn], hd[maxn], ver[maxn << 1], nxt[maxn << 1];
ll f[maxn], ans;
inline void adde(int u,int v) {
ver[++cnte] = v; nxt[cnte] = hd[u];
hd[u] = cnte; return;
}
inline void cmax(ll& a,ll b) {
if(a < b)
a = b;
}
void dfs1(int u,int pa) {
sz[u] = 1;
for(int i = hd[u];~i;i = nxt[i]) {
int v = ver[i];
if(v == pa)
continue;
dfs1(v,u);
sz[u] += sz[v];
}
f[1] += sz[u];
}
void dfs2(int u,int pa) {
for(int i = hd[u];~i;i = nxt[i]) {
int v = ver[i];
if(v == pa)
continue;
f[v] = f[u] + n - 2 * sz[v];
cmax(ans,f[v]);
dfs2(v,u);
}
}
int main() {
memset(hd,-1,sizeof(hd));
scanf("%d",&n);
for(int i = 1, u, v;i < n;i++) {
scanf("%d %d",&u,&v);
adde(u,v);
adde(v,u);
}
dfs1(1,0);
dfs2(1,0);
printf("%lld\n",ans);
return 0;
}