总结
不太行,时间都花在 (T1) 上了,最后实现的细节有些问题还没有暴力分高。
搜索的剪枝还是要打的,万一过了呢?
A. 小A的树
分析
树上路径统计不难想到点分治。
二分一个权值 (val) ,转化为判定性问题,即树上长度大于等于 (val) 的路径是否大于 (k) 个。
提前建出点分树并把要用的东西存好每次点分治的时候就不用重新排序,复杂度就是 (logVnlogn)。
关键在于最后的统计路径,如果使用容斥的写法拿 (set) 维护删除和加入操作就会向我一样挂成 (50)。
原因是毒瘤出题人的数据会让你扔进 (1e7) 个数再删除 (1e7) 个数。
所以最后的一次分治要用不容斥的写法,复杂度是 (nlog^2n) 也没有关系。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<vector>
#include<set>
#define rg register
template<typename T>void read(rg T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=2e5+5,maxm=1e6+5;
int h[maxn],tot=1;
struct asd{
int to,nxt,val;
}b[maxn<<1];
void ad(rg int aa,rg int bb,rg int cc){
b[tot].to=bb;
b[tot].nxt=h[aa];
b[tot].val=cc;
h[aa]=tot++;
}
int siz[maxn],maxsiz[maxn],rt,totsiz,n,k,zx,rk;
bool vis[maxn];
void getroot(rg int now,rg int lat){
siz[now]=1,maxsiz[now]=0;
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || vis[u]) continue;
getroot(u,now);
siz[now]+=siz[u];
maxsiz[now]=std::max(maxsiz[now],siz[u]);
}
maxsiz[now]=std::max(maxsiz[now],totsiz-siz[now]);
if(maxsiz[now]<maxsiz[rt]) rt=now;
}
std::vector<int> g[maxn],vec[maxn];
std::vector<long long>ld[maxm];
long long l,r,mids,cnt,sta2[maxn],sta[maxn];
int tp2,tp;
void dfs(rg int now,rg int lat,rg long long dis){
ld[rk].push_back(dis);
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat || vis[u]) continue;
dfs(u,now,dis+b[i].val);
}
}
void predfs(rg int now){
vis[now]=1,rk++;
vec[now].push_back(rk);
dfs(now,0,0);
std::sort(ld[rk].begin(),ld[rk].end());
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(vis[u]) continue;
rk++;
vec[now].push_back(rk);
dfs(u,now,b[i].val);
std::sort(ld[rk].begin(),ld[rk].end());
}
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(vis[u]) continue;
totsiz=siz[u],rt=0;
getroot(u,now);
g[now].push_back(rt);
predfs(rt);
}
}
void js(rg int xs){
rg int now=1,tmp;
for(rg int i=tp2;i>=1;i--){
while(sta2[i]+sta2[now]<mids && now<=tp2) now++;
if(now>i) return;
cnt+=(i-now)*xs;
}
}
void solve(rg int now){
if(cnt>=k) return;
rg int tmp=vec[now][0];
tp2=0;
for(rg int i=0;i<ld[tmp].size();i++) sta2[++tp2]=ld[tmp][i];
if(tp2>1) js(1);
for(rg int i=1;i<vec[now].size();i++){
tmp=vec[now][i],tp2=0;
for(rg int j=0;j<ld[tmp].size();j++) sta2[++tp2]=ld[tmp][j];
if(tp2>1) js(-1);
}
for(rg int i=0;i<g[now].size();i++) solve(g[now][i]);
}
bool jud(){
cnt=0,solve(zx);
return cnt>=k;
}
long long maxdis;
int jl;
void getdis(rg int now,rg int lat,rg long long ndis){
if(ndis>maxdis){
maxdis=ndis;
jl=now;
}
for(rg int i=h[now];i!=-1;i=b[i].nxt){
rg int u=b[i].to;
if(u==lat) continue;
getdis(u,now,ndis+b[i].val);
}
}
long long getzj(){
maxdis=-1;
getdis(1,0,0);
rg int tmp=jl;
maxdis=-1;
getdis(tmp,0,0);
return maxdis;
}
std::multiset<long long> s;
#define sit std::multiset<long long>::iterator
void init(){
rg sit it;
rg long long tmp=*--s.end();
for(rg int i=tp2;i>=1;i--){
if(tmp+sta2[i]<mids) return;
it=s.lower_bound(mids-sta2[i]);
for(;it!=s.end();++it) sta[++tp]=*it+sta2[i];
}
}
void solve2(rg int now){
rg int tmp=vec[now][0];
tp2=0;
s.insert(0);
for(rg int i=1;i<vec[now].size();i++){
tmp=vec[now][i],tp2=0;
for(rg int j=0;j<ld[tmp].size();j++) sta2[++tp2]=ld[tmp][j];
init();
for(rg int j=0;j<ld[tmp].size();j++) s.insert(ld[tmp][j]);
}
s.clear();
for(rg int i=0;i<g[now].size();i++) solve2(g[now][i]);
}
int main(){
memset(h,-1,sizeof(h));
read(n),read(k);
rg int aa,bb,cc;
l=0x3f3f3f3f3f3f3f3f;
for(rg int i=1;i<n;i++){
read(aa),read(bb),read(cc);
ad(aa,bb,cc),ad(bb,aa,cc);
l=std::min(l,1LL*cc);
}
r=getzj();
rt=0,maxsiz[0]=0x3f3f3f3f,totsiz=n;
getroot(1,0);
zx=rt;
predfs(zx);
while(l<=r){
mids=(l+r)>>1;
if(jud()) l=mids+1;
else r=mids-1;
}
cnt=0;
mids=r+1;
solve2(zx);
std::sort(sta+1,sta+tp+1);
std::reverse(sta+1,sta+tp+1);
while(tp<k) sta[++tp]=mids-1;
for(rg int i=1;i<=k;i++) printf("%lld
",sta[i]);
return 0;
}
B. 小B的序列
分析
大概类似于势能分析线段树。
分开考虑每一位,如果每一个数的当前位都相等,那么无论进行什么操作整体的变化都是相同的。
如果当前这一位上既有 (0) 又有 (1),那么进行或 (1) 或者于 (0) 的操作是没有影响的,否则就要分开递归。
快速判断整体变化是否相同的方法就是记录区间的按位或和和按位与和,如果这两个东西的变化量相同就可以对于区间整体打标记。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#define rg register
template<typename T>void read(rg T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=1e6+5;
int n,q,a[maxn];
struct trr{
int l,r,laz,sumor,sumand,mmax;
}tr[maxn];
void push_up(rg int da){
tr[da].mmax=std::max(tr[da<<1].mmax,tr[da<<1|1].mmax);
tr[da].sumand=tr[da<<1].sumand&tr[da<<1|1].sumand;
tr[da].sumor=tr[da<<1].sumor|tr[da<<1|1].sumor;
}
void push_down(rg int da){
if(!tr[da].laz) return;
tr[da<<1].mmax+=tr[da].laz,tr[da<<1].sumor+=tr[da].laz,tr[da<<1].sumand+=tr[da].laz,tr[da<<1].laz+=tr[da].laz;
tr[da<<1|1].mmax+=tr[da].laz,tr[da<<1|1].sumor+=tr[da].laz,tr[da<<1|1].sumand+=tr[da].laz,tr[da<<1|1].laz+=tr[da].laz;
tr[da].laz=0;
}
void build(rg int da,rg int l,rg int r){
tr[da].l=l,tr[da].r=r;
if(l==r){
tr[da].sumor=tr[da].sumand=tr[da].mmax=a[l];
return;
}
rg int mids=(l+r)>>1;
build(da<<1,l,mids);
build(da<<1|1,mids+1,r);
push_up(da);
}
int cx(rg int da,rg int l,rg int r){
if(tr[da].l>=l && tr[da].r<=r) return tr[da].mmax;
push_down(da);
rg int mids=(tr[da].l+tr[da].r)>>1,nans=-1;
if(l<=mids) nans=std::max(nans,cx(da<<1,l,r));
if(r>mids) nans=std::max(nans,cx(da<<1|1,l,r));
return nans;
}
void ador(rg int da,rg int l,rg int r,rg int val){
if(tr[da].l>=l && tr[da].r<=r && (tr[da].sumand|val)-tr[da].sumand==(tr[da].sumor|val)-tr[da].sumor){
tr[da].laz+=(tr[da].sumand|val)-tr[da].sumand;
tr[da].sumand|=val,tr[da].sumor|=val,tr[da].mmax|=val;
return;
}
push_down(da);
rg int mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) ador(da<<1,l,r,val);
if(r>mids) ador(da<<1|1,l,r,val);
push_up(da);
}
void adand(rg int da,rg int l,rg int r,rg int val){
if(tr[da].l>=l && tr[da].r<=r && (tr[da].sumand&val)-tr[da].sumand==(tr[da].sumor&val)-tr[da].sumor){
tr[da].laz+=(tr[da].sumand&val)-tr[da].sumand;
tr[da].sumand&=val,tr[da].sumor&=val,tr[da].mmax&=val;
return;
}
push_down(da);
rg int mids=(tr[da].l+tr[da].r)>>1;
if(l<=mids) adand(da<<1,l,r,val);
if(r>mids) adand(da<<1|1,l,r,val);
push_up(da);
}
int main(){
read(n),read(q);
for(rg int i=1;i<=n;i++) read(a[i]);
build(1,1,n);
rg int aa,bb,cc,dd;
for(rg int i=1;i<=q;i++){
read(aa),read(bb),read(cc);
if(aa!=3) read(dd);
if(aa==1){
adand(1,bb,cc,dd);
} else if(aa==2){
ador(1,bb,cc,dd);
} else {
printf("%d
",cx(1,bb,cc));
}
}
return 0;
}
C. 小C的利是
分析
题解的倒数第五行应该是 (S equiv 0 pmod k)
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<cmath>
#include<cstdlib>
#include<ctime>
#include<vector>
#define rg register
template<typename T>void read(rg T &x){
x=0;rg int fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
x*=fh;
}
const int maxn=1e3+5;
int n,k,a[maxn][maxn],r[maxn][maxn],mp[maxn][maxn],mod,g;
inline int addmod(rg int now1,rg int now2){
return now1+=now2,now1>=mod?now1-mod:now1;
}
inline int delmod(rg int now1,rg int now2){
return now1-=now2,now1<0?now1+mod:now1;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=mod?now1%mod:now1;
}
inline int ksm(rg int ds,rg int zs){
rg int nans=1;
while(zs){
if(zs&1) nans=mulmod(nans,ds);
ds=mulmod(ds,ds);
zs>>=1;
}
return nans;
}
bool isprime(rg int now){
if(now==1) return 0;
for(rg int i=2;i*i<=now;i++){
if(now%i==0) return 0;
}
return 1;
}
int gsxy(){
rg int ans=1;
for(rg int i=1;i<=n;i++){
for(rg int j=i+1;j<=n;j++){
if(!mp[i][i] && mp[j][i]){
std::swap(mp[i],mp[j]);
ans=delmod(mod,ans);
break;
}
}
rg int ny=ksm(mp[i][i],mod-2);
for(rg int j=i+1;j<=n;j++){
rg int cs=mulmod(mp[j][i],ny);
for(rg int k=i;k<=n;k++){
mp[j][k]=delmod(mp[j][k],mulmod(mp[i][k],cs));
}
}
}
for(rg int i=1;i<=n;i++) ans=mulmod(ans,mp[i][i]);
return ans;
}
int sum;
int main(){
srand(time(0));
read(n),read(k);
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n;j++){
read(a[i][j]);
}
}
mod=k*317913+1;
while(!isprime(mod)) mod+=k;
for(g=2;;g++){
if(ksm(g,k)==1) break;
}
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n;j++){
r[i][j]=ksm(i+j,j);
}
}
for(rg int now=0,x=1;now<k;now++,x=mulmod(x,g)){
for(rg int i=1;i<=n;i++){
for(rg int j=1;j<=n;j++){
if(a[i][j]!=-1){
mp[i][j]=mulmod(r[i][j],ksm(x,a[i][j]));
} else {
mp[i][j]=0;
}
}
}
sum=addmod(sum,gsxy());
}
if(sum) printf("Yes
");
else printf("No
");
return 0;
}