D:
多次找后缀,显然是用倍增处理。
关键是找到一次后缀。
我们倒叙处理,类似单调队列维护:序列,id递增,值递增。
b数组,点i后面第一个大于点i的位置。如果前面的数小,直接b[i]=i+1,否则我们查找b[b[i+1]],循环直到找到第一个大于当前数的位置。中间较小的数都被弹出,将来肯定不会查找到他们(因为a[i],不仅大于它们,而且id小于他们,类比单调队列就很容易想了)
每次查找。
每个点最多访问2次。On即可处理除所有点的后缀。
然后用倍增处理询问即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
int a[M];
int b[M];//b[i]:第一个大于a[i]的位置
int vs[M];
int f[M][30];
int main()
{
int T;
cin>>T;
while(T--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
memset(f,0,sizeof(f));
a[n+1]=2e9;
for(int i=n;i;i--)
{
if(a[i]<a[i+1])b[i]=i+1;
else
{
int tp=b[i+1];
while(a[i]>=a[tp])tp=b[tp];
b[i]=tp;//相当于单调队列,我们始终维护id上升且a[i]变大的序列
}
if(b[i]==n+1)f[i][0]=0;
else f[i][0]=b[i];
// cout<<i<<" "<<f[i][0]<< " "<<b[i]<<endl;
}
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
int tp=x;y--;
for(int i=20;i>=0;i--)
{
if((1<<i)<=y)
{
tp=f[tp][i];
y-=(1<<i);
}
}
if(tp>0)printf("%d\n",tp);
else puts("-1");
}
}
return 0;
}
G:
简单的换根dp
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2e5+7;
int head[M],cnt;
struct EDGE{int to,nxt,w;}ee[M*2];
void add(int x,int y){ee[++cnt].nxt=head[x],ee[cnt].to=y,head[x]=cnt;}
ll w[M];
ll sm[M];//子树点权和
ll ans[M];//最终以i为根的结果
ll tp;//以1为根的结果
void dfs(int x,int fa)
{
sm[x]=w[x];
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(y==fa)continue;
dfs(y,x);
sm[x]+=sm[y];
}
tp+=sm[x];
}
void gao(int x,int fa)
{
//cout<<"=-- "<<x<<endl;
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
if(y==fa)continue;
ans[y]=ans[x]-sm[y]+(sm[1]-sm[y]);
gao(y,x);
}
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n,u,v;
scanf("%d",&n);
cnt=tp=0;
memset(head,0,sizeof(head));
memset(sm,0,sizeof(sm));
memset(ans,0,sizeof(ans));
memset(w,0,sizeof(w));
for(int i=1;i<=n;i++)scanf("%lld",&w[i]);
for(int i=1;i<n;i++)scanf("%d%d",&u,&v),add(u,v),add(v,u);
dfs(1,0);
ans[1]=tp;
gao(1,0);
for(int i=1;i<n;i++)printf("%lld ",ans[i]);
printf("%lld\n",ans[n]);
}
return 0;
}
H:
拆改题目式子后有:
a[i]*b[i]*i*(n-i+1), b[i]*i*(n-i+1)是已知量,小乘大中和结果即可。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 2e5+7;
const int mod =1e9+7;
ll a[M];
ll b[M],p[M];
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
ll ans=0;
for(int i=1;i<=n;i++)
p[i]=b[i]*i*(n-i+1);
sort(p+1,p+1+n);
sort(a+1,a+1+n);
for(int i=1;i<=n;i++)
ans=(ans+a[i]*(p[n-i+1]%mod)%mod)%mod;
printf("%lld\n",ans);
}
return 0;
}
I:
凑数,显然二进制凑数,用量最少,总和可刚好等于n。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define ls (o<<1)
#define rs (o<<1|1)
#define pb push_back
const double PI= acos(-1.0);
const int M = 1e5+7;
ll sm[M];
int main()
{
int t;
cin>>t;
sm[1]=1;
ll tp=1;
for(int i=2;i<=100;i++)
{
tp*=2;
sm[i]=sm[i-1]+tp;
if(sm[i]>=1e18)break;
}
while(t--)
{
ll k;
scanf("%lld",&k);
vector<ll>v;
int id=1;
for(int i=1;i<=100;i++)
{
if(sm[i]>k)
{
id=i-1;
// cout<<i<<" "<<sm[i]<<" "<<endl;
k-=sm[i-1];
break;
}
}
tp=1;
if(k)v.pb(k);
for(int i=1;i<=id;i++)v.pb(tp),tp*=2;
printf("%lld\n",v.size());
printf("%lld",v[0]);
for(int i=1;i<v.size();i++)
{
printf(" %lld",v[i]);
}
puts("");
}
return 0;
}
M:
刚开始看成sum_i sum_j f(i,j)了
其实是异或和。
无论哪种题意,先考虑f(i,j)。
预处理出f(1,j) : f[j]
f(i,j)=f(1,j)^f(1,i);//比较显然,可以在纸上画一画。
然后考虑这一题:
拆开后发现:(n-1)个f[i]异或
即每个f[i]都有n-1个,这(n-1)*n个数进行异或。
显然是看n-1是否是奇数。奇数结果为:f1^f2^……^fn 偶数为0
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int M = 2e5+7;
int head[M],cnt;
struct EDGE{int to,nxt;ll w;}ee[M*2];
void add(int x,int y,ll w){ee[++cnt].nxt=head[x],ee[cnt].w=w,ee[cnt].to=y,head[x]=cnt;}
ll f[M];//f(1,i)的值
ll ans;
void dfs(int x,int fa)
{
for(int i=head[x];i;i=ee[i].nxt)
{
int y=ee[i].to;
ll w=ee[i].w;
if(y==fa)continue;
f[y]=f[x]^w;
dfs(y,x);
}
}
void init(){
cnt=ans=0;
memset(head,0,sizeof(head));
memset(f,0,sizeof(f));
}
int main()
{
int T;
cin>>T;
while(T--)
{
int n,u,v;
ll w;
scanf("%d",&n);
init();
for(int i=1;i<n;i++)scanf("%d%d%lld",&u,&v,&w),add(u,v,w),add(v,u,w);
dfs(1,0);
ll tp=0;
for(int i=1;i<=n;i++)tp^=f[i];
if((n-1)&1)ans=tp;
else ans=0;
printf("%lld\n",ans);
}
return 0;
}
/*
1421
5
1 2 1
2 3 2
3 4 3
4 5 4
*/