传送门:
Problem A
温暖的签到题,打个表就行了。
吐槽一下:出题人并没有说用什么格式输出,害得我输出一行怒得一pe
#include <bits/stdc++.h>
using namespace std;
int main()
{
cout<<6<<endl;
cout<<28<<endl;
cout<<496<<endl;
cout<<8128<<endl;
cout<<33550336<<endl;
return 0;
}
Problem B
Problem C
Problem D
Problem E
Problem F
Problem G
Problem H
规律题,经过推倒发现答案为:。
快速幂搞搞就万事了。
#include <iostream>
#include <cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
ll powm(ll x,ll n){
ll res=1;
while (n){
if (n&1) res=res*x%mod;
n>>=1;
x=x*x%mod;
}
return res;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
ll n,ans=0;
cin>>n;
if (n==1) ans=1;
else ans=powm(3,n-2)*4%mod;
cout<<ans<<endl;
return 0;
}
Problem I
题意:
给你一个长度为的序列,让你找到一个区间,使得最大
其中,
分析:
如果不能取负数,则是的原题,我们用单调栈对每一个处理出以为最小值,最左/最右能扩展到的位置,之后直接贪心扫即可。
而在这个题中,因为涉及负数×负数的情况,则我们不能直接累加某段区间的和。
我们考虑用线段树去记录区间前缀和的最大值以及最小值。我们可以贪心的去思考,倘若当前的大于,则显然是取区间中的最大值,以及区间中的最小值,即为以为最小值的最大区间和。同理,对于的情况,我们只需尽量取区间中的最小区间和即可。
#include <bits/stdc++.h>
using namespace std;
const int maxn=500005;
int a[maxn];
long long sum[maxn];
int n,m;
struct node
{
int l,r;
long long mx,add,mn;
}tr[maxn*4];
void push_up(int k){
tr[k].mx=max(tr[k<<1].mx,tr[k<<1|1].mx);
tr[k].mn=min(tr[k<<1].mn,tr[k<<1|1].mn);
}
void build(int k,int l,int r)
{
tr[k].l=l,tr[k].r=r;
tr[k].add=0;
if(l==r){
tr[k].mx=sum[l];
tr[k].mn=sum[l];
return;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
push_up(k);
}
void pushdown(int k)
{
if(tr[k].add!=0)
{
tr[k<<1].add+=tr[k].add;
tr[k<<1].mx+=tr[k].add;
tr[k<<1].mn+=tr[k].add;
tr[k<<1|1].add+=tr[k].add;
tr[k<<1|1].mx+=tr[k].add;
tr[k<<1|1].mn+=tr[k].add;
tr[k].add=0;
}
}
void update(int k,int s,int t,int v)
{
int l=tr[k].l,r=tr[k].r;
if(l>t||r<s) return;
if(l>=s&&r<=t)
{
tr[k].mx+=v;
tr[k].mn+=v;
tr[k].add+=v;
return;
}
pushdown(k);
update(k<<1,s,t,v);
update(k<<1|1,s,t,v);
push_up(k);
}
long long query(int k,int s,int t)
{
int l=tr[k].l,r=tr[k].r;
if(l>t||r<s) return 0;
if(l>=s&&r<=t) return tr[k].mx;
pushdown(k);
return max(query(k<<1,s,t),query(k<<1|1,s,t));
}
long long query1(int k,int s,int t)
{
int l=tr[k].l,r=tr[k].r;
if(l>t||r<s) return 0;
if(l>=s&&r<=t) return tr[k].mn;
pushdown(k);
return min(query1(k<<1,s,t),query1(k<<1|1,s,t));
}
void read(int &ret){
ret=0;
char ch=getchar();
int flag=1;
while(ch>'9'||ch<'0'){if(ch=='-')flag=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){
ret=ret*10+ch-'0';
ch=getchar();
}
ret*=flag;
}
int L[maxn],R[maxn];
long long ans1[maxn],ans2[maxn];
int main()
{
//freopen("in.txt","r",stdin);
read(n);
//cin>>n;
for(int i=1;i<=n;i++){
read(a[i]);
//cin>>a[i];
sum[i]=sum[i-1]+a[i];
L[i]=R[i]=i;
}
a[0]=a[n+1]=-0x3f3f3f3f;
for(int i=1;i<=n;i++){
while(a[i]<=a[L[i]-1])
L[i]=L[L[i]-1];
}
for(int i=n;i>=1;i--){
while(a[i]<=a[R[i]+1])
R[i]=R[R[i]+1];
}
int r=1;
a[0]=a[n+1]=0;
build(1,1,n);
for(int i=1;i<=n;i++){
update(1,i,n,-a[i-1]);
if(a[i]>=0)ans1[i]=query(1,i,R[i]);
else ans1[i]=query1(1,i,R[i]);
}
for(int i=n;i;i--){sum[i]=sum[i+1]+a[i];}
build(1,1,n);
for(int i=n;i;i--){
update(1,1,i,-a[i+1]);
if(a[i]>=0)ans2[i]=query(1,L[i],i);
else ans2[i]=query1(1,L[i],i);
}
long long mx=-0x3f3f3f3f3f3f3f3f;
for(int i=1;i<=n;i++){
mx=max(mx,(ans1[i]+ans2[i]-a[i])*a[i]);
}
printf("%lld
",mx);
return 0;
}
Problem J
题意:
给你一棵有个节点的树,每条边上有个边权,现在有个询问,问你在节点到节点的路径中,有多少条边的边权是大于的。
分析:
考虑去二分答案。如果用二分去解的话,这个问题就转化为让你找到一个最小的,使得条边的权值都大于。则显然我们只需要求这段路径上的第大的路径的值,并判断第大的路径的值是否大于即可。
因此我们要求的就是一个经典的树上路径第大的问题,因此我们可以用主席树去解决。
总体的时间复杂度为:
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200010;
const int M = MAXN * 40;
int n,q,m,TOT;
int a[MAXN], t[MAXN];
int T[M], lson[M], rson[M], c[M];
void Init_hash()
{
for(int i = 1; i <= n;i++)
t[i] = a[i];
sort(t+1,t+1+n);
m = unique(t+1,t+n+1)-t-1;
}
int build(int l,int r)
{
int root = TOT++;
c[root] = 0;
if(l != r)
{
int mid = (l+r)>>1;
lson[root] = build(l,mid);
rson[root] = build(mid+1,r);
}
return root;
}
int Hash(int x)
{
return lower_bound(t+1,t+1+m,x) - t;
}
int update(int root,int pos,int val)
{
int newroot = TOT++, tmp = newroot;
c[newroot] = c[root] + val;
int l = 1, r = m;
while( l < r)
{
int mid = (l+r)>>1;
if(pos <= mid)
{
lson[newroot] = TOT++; rson[newroot] = rson[root];
newroot = lson[newroot]; root = lson[root];
r = mid;
}
else
{
rson[newroot] = TOT++; lson[newroot] = lson[root];
newroot = rson[newroot]; root = rson[root];
l = mid+1;
}
c[newroot] = c[root] + val;
}
return tmp;
}
int query(int left_root,int right_root,int LCA,int k)
{
int lca_root = T[LCA];
int pos = Hash(a[LCA]);
int l = 1, r = m;
while(l < r)
{
int mid = (l+r)>>1;
int tmp = c[lson[left_root]] + c[lson[right_root]] - 2*c[lson[lca_root]] + (pos >= l && pos <= mid);
if(tmp >= k)
{
left_root = lson[left_root];
right_root = lson[right_root];
lca_root = lson[lca_root];
r = mid;
}
else
{
k -= tmp;
left_root = rson[left_root];
right_root = rson[right_root];
lca_root = rson[lca_root];
l = mid + 1;
}
}
return l;
}
int rmq[2*MAXN];
struct ST
{
int mm[2*MAXN];
int dp[2*MAXN][20];
void init(int n)
{
mm[0] = -1;
for(int i = 1;i <= n;i++)
{
mm[i] = ((i&(i-1)) == 0)?mm[i-1]+1:mm[i-1];
dp[i][0] = i;
}
for(int j = 1; j <= mm[n];j++)
for(int i = 1; i + (1<<j) - 1 <= n; i++)
dp[i][j] = rmq[dp[i][j-1]] < rmq[dp[i+(1<<(j-1))][j-1]]?dp[i][j-1]:dp[i+(1<<(j-1))][j-1];
}
int query(int a,int b)
{
if(a > b)swap(a,b);
int k = mm[b-a+1];
return rmq[dp[a][k]] <= rmq[dp[b-(1<<k)+1][k]]?dp[a][k]:dp[b-(1<<k)+1][k];
}
};
struct Edge
{
int to,next,val;
};
Edge edge[MAXN*2];
int tot,head[MAXN];
int F[MAXN*2];
int P[MAXN];
int cnt;
ST st;
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
}
void addedge(int u,int v,int val)
{
edge[tot].to = v;
edge[tot].next = head[u];
edge[tot].val=val;
head[u] = tot++;
}
void dfs(int u,int pre,int dep)
{
F[++cnt] = u;
rmq[cnt] = dep;
P[u] = cnt;
for(int i = head[u];i != -1;i = edge[i].next)
{
int v = edge[i].to;
if(v == pre)continue;
dfs(v,u,dep+1);
F[++cnt] = u;
rmq[cnt] = dep;
}
}
void LCA_init(int root,int node_num)
{
cnt = 0;
dfs(root,root,0);
st.init(2*node_num-1);
}
int query_lca(int u,int v)
{
return F[st.query(P[u],P[v])];
}
void dfs_build(int u,int pre)
{
int pos = Hash(a[u]);
T[u] = update(T[pre],pos,1);
for(int i = head[u]; i != -1;i = edge[i].next)
{
int v = edge[i].to;
if(v == pre)continue;
dfs_build(v,u);
}
}
bool check(int u,int v,int k,int val){
if(t[query(T[u],T[v],query_lca(u,v),k)]>val) return 1;
return 0;
}
int Find(int u,int v,int l,int r,int k){
int ret;
while(l<=r){
int mid=(l+r)>>1;
if(check(u,v,mid,k)){
ret=mid;
r=mid-1;
}else l=mid+1;
}
if(a[query_lca(u,v)]<=k) ret-=1;
return ret;
}
void dfs(int u,int fa){
for(int i=head[u];~i;i=edge[i].next){
int to=edge[i].to;
if(to==fa) continue;
a[to]=edge[i].val;
dfs(to,u);
}
}
int main()
{
//freopen("in.txt","r",stdin);
while(scanf("%d%d",&n,&q) == 2)
{
TOT = 0;
int u,v,vv;
a[1]=0x3f3f3f3f;
init();
for(int i = 1;i < n;i++)
{
scanf("%d%d%d",&u,&v,&vv);
addedge(u,v,vv);
addedge(v,u,vv);
}
dfs(1,-1);
Init_hash();
LCA_init(1,n);
T[n+1] = build(1,m);
dfs_build(1,n+1);
int k;
while(q--)
{
scanf("%d%d%d",&u,&v,&k);
printf("%d
",Find(u,v,1,n,k)-1);
}
return 0;
}
return 0;
}
Problem K
找规律+前缀亦或和
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;
const int maxn=1e5+7;
unsigned int a[maxn],b[maxn][4];
namespace fastIO {
#define BUF_SIZE 100000
//fread -> read
bool IOerror = 0;
inline char nc() {
static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
if(p1 == pend) {
p1 = buf;
pend = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pend == p1) {
IOerror = 1;
return -1;
}
}
return *p1++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '
' || ch == '
' || ch == ' ';
}
inline void read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror) return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
inline void read(unsigned int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror) return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace fastIO;
int main(){
int t;
read(t);
while (t--){
int n,q,l,r;
read(n);
for (int i=0;i<n;i++) {
read(a[i]);
b[i][0]=b[i][1]=b[i][2]=b[i][3]=0;
}
int m=(n+3)/4+1;
for (int i=0;i<n;i++)
b[i/4+1][i%4]=a[i];
// for (int i=0;i<4;i++){
// for (int j=0;j<=m+1;j++) cout<<b[j][i]<<' ';cout<<endl;
// }
for (int i=1;i<=m+1;i++) {
for (int j=0;j<4;j++)
b[i][j]^=b[i-1][j];
}
read(q);
while (q--){
read(l);read(r);
unsigned ans=0;
int tmp=(r-l)%4;
l--;r--;
if (tmp==3) ans=0;
else if (tmp==2) {
int ll=l+1,rr=r-1;
ans=b[rr/4+1][ll%4]^b[ll/4][ll%4];
}else if (tmp==1){
int ll=l,rr=r-1;
ans=b[rr/4+1][ll%4]^b[ll/4][ll%4] ^b[(rr+1)/4+1][(ll+1)%4]^b[(ll+1)/4][(ll+1)%4];
}else if (tmp==0){
int ll=l,rr=r;
ans=b[rr/4+1][ll%4]^b[ll/4][ll%4];
}
printf("%u
",ans);
}
}
return 0;
}
Problem L
Problem M
题意:
给你一个串以及个串,问你这个串是否是串的子序列。
分析:
直接暴力匹配显然会T,而又根据子序列的性质(下一个字符要匹配的字符必定是在原串离上一个匹配成果的位置的最近的位置上),我们可以考虑对原串进行预处理。
我们设为:在原串位置上,离字符最近的位置。我们可以用的时间预处理出来这个,而在我们进行个串的匹配时,我们不断在数组上去跳转即可。
代码:
#include <bits/stdc++.h>
#define maxn 100005
using namespace std;
char str[100005],p[1005];
int f[100005][300];
int len,n;
void init(){
for(int i=0;i<len;i++){
int flag=str[i];
for(int j=i;j>=0;j--){
if(f[j][flag]!=-1) break;
f[j][flag]=i;
}
}
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%s",str);
scanf("%d",&n);
len=strlen(str);
memset(f,-1,sizeof(f));
init();
while(n--){
scanf("%s",p);
int l=strlen(p);
int cur=0;
bool flag=0;
for(int i=0;i<l;i++){
if(f[cur][p[i]]==-1){
flag=1;
break;
}
cur=f[cur][p[i]]+1;
}
if(flag)puts("NO");
else puts("YES");
}
return 0;
}