這裏放一下題解
day1
T1
根據題目描述遞歸,也可以使用這樣的神仙做法,注意即可。
時間複雜度:或
當場代碼:
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll unsigned long long
#define db double
const int N=7;
const int INF=1e9+7;
ll n,k;
int main(){
scanf("%llu%llu",&n,&k);
for(ll i=n;i;i--){
if(k<(((ll)1)<<((ll)(i-1))))putchar('0');
else{
k-=(((ll)1)<<((ll)(i-1)));
k^=((((ll)1)<<((ll)(i-1)))-1);
putchar('1');
}
}
puts("");
return 0;
}
T2
首先把左括號看成,右括號看成,做樹上前綴和。然後一個點如果是右括號,則存在以這個點爲結尾的括號序列,遇到這樣的點可以向上跳到第一個前綴和相同的點,當前點結尾的合法括號數爲這個點。用一個桶記錄,最後再樹上前綴和即可。
時間複雜度:
當場代碼:
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
void read(ll &x){
char c=getchar(); x=0;
while(c<'0'||c>'9')c=getchar();
while(c<='9'&&c>='0')x=x*10ll+c-'0',c=getchar();
}
const ll N=5e5+7;
const ll INF=1e9+7;
char ss[N];
ll pos[N],fr[N*2],s[N],fa[N],up[N],d[N];
struct edge{ll v,next;}e[N];
ll n,num=0,ans=0;
void add(ll x,ll y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
void dfs1(ll x){
if(ss[x]=='(')s[x]=s[fa[x]]+1;
else s[x]=s[fa[x]]-1;
up[x]=fr[s[x]+N];
fr[s[x]+N]=x;
repG(i,x)dfs1(e[i].v);
fr[s[x]+N]=up[x];
}
void dfs2(ll x){
if(s[fa[x]]<s[x])d[x]=0;
else if(up[x]<0)d[x]=0;
else d[x]=d[up[x]]+1;
repG(i,x)dfs2(e[i].v);
}
void dfs3(ll x){
d[x]+=d[fa[x]];
ans^=(d[x]*x);
repG(i,x)dfs3(e[i].v);
}
int main(){
memset(fr,-1,sizeof(fr));
memset(pos,-1,sizeof(pos));
scanf("%lld",&n);
scanf("%s",ss+1);
REP(i,2,n){
read(fa[i]);
add(fa[i],i);
}
fr[N]=0;
dfs1(1);
dfs2(1);
dfs3(1);
printf("%lld\n",ans);
return 0;
}
T3
基本思路是按位貪心,然後我們需要確定的事情就是當前能把一個數移動到哪些點。我們考慮確定了一個數的路徑以後會發生什麼,那就是對於路徑上相鄰的兩條邊(有公共點),對於它們的公共點來說,這兩條邊的操作順序是相鄰的。我們還發現如果一個點的所有出邊的操作順序確定合法了,那麼對於全局來說一定會有合法方案,所以我們就對於每個點的出邊維護一個鏈表,每次只需查詢能不能從一條邊進入一條邊走出,對於第一次操作和最後一次操作也維護一下即可。
時間複雜度:
當場代碼:
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<time.h>
#include<bitset>
#include<vector>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(int i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(int i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const int N=2007;
const int INF=1e9+7;
int T,n,num,mn;
int g[N],pos[N],ne[N*2],fr[N],dg[N];
struct edge{int v,next,ot;}e[N*2];
void add(int x,int y){e[num]=(edge){y,pos[x],1}; ne[num]=num; pos[x]=num++;}
void dfs1(int x,int eg){
if(fr[x]==-1&&(ne[eg]!=-1||dg[x]==2))mn=min(mn,x);
repG(i,x){
if(!e[i].ot)continue;
if(i==ne[eg])continue;
if((fr[x]!=i||ne[eg]!=-1)||dg[x]==2)dfs1(e[i].v,i^1);
}
}
bool dfs2(int x,int eg){
if(fr[x]==-1&&(ne[eg]!=-1||dg[x]==2)){
if(x==mn){
fr[x]=ne[eg];
dg[x]--;
return 1;
}
}
repG(i,x){
if(!e[i].ot)continue;
if(i==ne[eg])continue;
if((fr[x]!=i||ne[eg]!=-1)||dg[x]==2){
if(dfs2(e[i].v,i^1)){
e[i].ot=0;
if(fr[x]!=i){
repG(j,x)if(ne[j]==i)ne[j]=ne[eg];
}
else fr[x]=ne[eg];
dg[x]--;
return 1;
}
}
}
return 0;
}
int main(){
scanf("%d",&T);
while(T--){
num=0;
memset(pos,-1,sizeof(pos));
scanf("%d",&n);
rep(i,n)scanf("%d",&g[i]);
rep(i,n)dg[i]=2,fr[i]=-1;
rep(i,n-1){
int x,y; scanf("%d%d",&x,&y);
dg[x]++;
dg[y]++;
add(x,y); add(y,x);
}
rep(i,n){
mn=INF;
repG(j,g[i]){
if(!e[j].ot)continue;
if(fr[g[i]]!=j||dg[g[i]]==2)dfs1(e[j].v,j^1);
}
repG(j,g[i]){
if(!e[j].ot)continue;
if(fr[g[i]]!=j||dg[g[i]]==2){
if(dfs2(e[j].v,j^1)){
e[j].ot=0;
if(fr[g[i]]!=j)repG(k,g[i])if(ne[k]==j)ne[k]=-1;
dg[g[i]]--;
}
}
}
printf("%d ",mn);
}
puts("");
}
return 0;
}
day2
T1
我們發現不符合要求的食材最多隻有一種,於是可以先進行補集轉化,然後枚舉不符合要求的食材,這時我們可以把其它食材看成一種,做一個揹包,發現限制是兩種食材的差,所以令表示前個烹飪方法,兩種食材差值爲的方案數,揹包即可。
時間複雜度:
代碼:
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=107;
const ll M=2007;
const ll INF=1e9+7;
const ll mod=998244353;
ll a[N][M],s[N],u[N],v[N],f[N][N*2];
ll n,m,ans=1;
int main(){
scanf("%lld%lld",&n,&m);
rep(i,n)rep(j,m)scanf("%lld",&a[i][j]);
rep(i,n){
s[i]=0;
rep(j,m)s[i]=(s[i]+a[i][j])%mod;
ans=ans*(s[i]+1)%mod;
}
ans--;
rep(i,m){
rep(j,n)u[j]=a[j][i];
rep(j,n)v[j]=(s[j]-a[j][i]+mod)%mod;
rep(j,n+1)rep0(k,N*2)f[j][k]=0;
f[1][N]=1;
rep(j,n){
rep0(k,N*2){
if(f[j][k]){
f[j+1][k]=(f[j+1][k]+f[j][k])%mod;
f[j+1][k+1]=(f[j+1][k+1]+f[j][k]*u[j])%mod;
f[j+1][k-1]=(f[j+1][k-1]+f[j][k]*v[j])%mod;
}
}
}
rep(j,n)ans=(ans-f[n+1][j+N]+mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
T2
經過觀察我們可以猜出一個性質,那就是最後一段最短的一定是更優的。粗略證明的話就是因爲代價是平方,而二次函數導數單調遞增,所以把後面的分給前面一定更優。然後我們令表示前個位置往前最短到存在合法劃分方案,轉移時可以用單調隊列優化。最後答案可能很大,這裏採用計算。
時間複雜度:
代碼:
#include<iostream>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cstdio>
#include<vector>
#include<bitset>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=4e7+7;
const ll INF=1e9+7;
const ll kk=1ll<<30;
void read(ll &x){
char c=getchar(); x=0;
while(c<'0'||c>'9')c=getchar();
while(c<='9'&&c>='0')x=x*10+c-'0',c=getchar();
}
ll Pow(ll x,ll y,ll mod){
ll ans=1,now=x;
while(y){
if(y&1)ans=ans*now%mod;
now=now*now%mod;
y>>=1;
}
return ans;
}
const ll T=1000000000;
struct gg{
ll x[4];
friend bool operator < (gg a,gg b){
for(ll i=3;i;i--)if(a.x[i]!=b.x[i])return a.x[i]<b.x[i];
return a.x[0]<b.x[0];
}
friend gg operator * (gg a,gg b){
gg c={{0,0,0,0}};
rep0(i,4)rep0(j,4)if(i+j<4)c.x[i+j]+=a.x[i]*b.x[j];
rep0(i,3){
c.x[i+1]+=c.x[i]/T;
c.x[i]%=T;
}
return c;
}
friend gg operator + (gg a,gg b){
gg c;
rep0(i,4)c.x[i]=a.x[i]+b.x[i];
rep0(i,3){
c.x[i+1]+=c.x[i]/T;
c.x[i]%=T;
}
return c;
}
friend gg operator - (gg a,gg b){
gg c;
rep0(i,4)c.x[i]=a.x[i]-b.x[i];
rep0(i,3){
while(c.x[i]<0){
c.x[i+1]--;
c.x[i]+=T;
}
}
return c;
}
};
ll n,ty,tp=1;
ll s[N],v[N];
int t[N],fr[N];
void print(gg r){
bool fl=0;
for(ll i=3;~i;i--){
if(fl)for(ll j=T/10;j;j/=10)putchar('0'+r.x[i]/j%10);
else{
if(r.x[i]){
printf("%lld",r.x[i]);
fl=1;
}
}
}
if(!fl)putchar('0');
}
int main(){
scanf("%lld%lld",&n,&ty);
if(!ty)rep(i,n)read(s[i]);
else{
ll X,Y,Z,m; scanf("%lld%lld%lld%lld%lld%lld",&X,&Y,&Z,&s[1],&s[2],&m);
REP(i,3,n)s[i]=(X*s[i-1]+Y*s[i-2]+Z)%kk;
ll ls=0;
rep(i,m){
ll l,r,p; read(p); read(l); read(r);
REP(j,ls+1,p)s[j]=s[j]%(r-l+1)+l;
ls=p;
}
}
rep(i,n)s[i]+=s[i-1];
t[1]=v[1]=0;
ll p=1;
rep(i,n){
while(p<tp&&v[p+1]<=s[i])p++;
fr[i]=t[p];
ll vv=2*s[i]-s[fr[i]];
while(v[tp]>vv)tp--;
t[++tp]=i;
v[tp]=vv;
if(p>tp)p=tp;
}
if(!ty){
ll ans=0;
for(ll i=n;i;i=fr[i])ans+=(s[i]-s[fr[i]])*(s[i]-s[fr[i]]);
printf("%lld\n",ans);
}
else{
ll mod1=998244353,mod2=1000000007,mod3=1004535809,mod4=19260817;
ll s1=0,s2=0,s3=0,s4=0,hh=0;
for(ll i=n;i;i=fr[i],hh++){
ll v1=(s[i]-s[fr[i]])%mod1,v2=(s[i]-s[fr[i]])%mod2;
ll v3=(s[i]-s[fr[i]])%mod3,v4=(s[i]-s[fr[i]])%mod4;
s1+=v1*v1;
s2+=v2*v2;
s3+=v3*v3;
s4+=v4*v4;
if(hh&8){
s1%=mod1;
s2%=mod2;
s3%=mod3;
s4%=mod4;
}
}
s1%=mod1;
s2%=mod2;
s3%=mod3;
s4%=mod4;
ll u1=s1*Pow(mod2*mod3%mod1*mod4%mod1,mod1-2,mod1)%mod1;
ll u2=s2*Pow(mod1*mod3%mod2*mod4%mod2,mod2-2,mod2)%mod2;
ll u3=s3*Pow(mod1*mod2%mod3*mod4%mod3,mod3-2,mod3)%mod3;
ll u4=s4*Pow(mod1*mod2%mod4*mod3%mod4,mod4-2,mod4)%mod4;
gg m1=(gg){{mod1%T,mod1/T,0,0}},m2=(gg){{mod2%T,mod2/T,0,0}};
gg m3=(gg){{mod3%T,mod3/T,0,0}},m4=(gg){{mod4%T,mod4/T,0,0}};
gg k1=(gg){{u1%T,u1/T,0,0}},k2=(gg){{u2%T,u2/T,0,0}};
gg k3=(gg){{u3%T,u3/T,0,0}},k4=(gg){{u4%T,u4/T,0,0}};
gg ans=k1*m2*m3*m4+k2*m1*m3*m4+k3*m1*m2*m4+k4*m1*m2*m3;
gg pp=m1*m2*m3*m4;
while(pp<ans)ans=ans-pp;
print(ans);
return 0;
}
return 0;
}
T3
這題做法很多。
我們可以考慮對每一條邊計算子樹內的中心和子樹外的重心,子樹內很好處理,我們對樹進行樹剖,最後中心一定在重鏈上,且一定是重兒子答案的祖先,維護指針掃描就好了。對於邊上方的部分,我們可以先在根節點的重鏈上二分,根據是否子樹中包含當前邊分成兩段。如果二分到這條邊的祖先,還要考慮一下次重兒子的重鏈。
時間複雜度:
代碼:
#include<iostream>
#include<cstring>
#include<cassert>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<time.h>
#include<algorithm>
using namespace std;
#define REP(i,x,y) for(ll i=x;i<=y;i++)
#define rep(i,n) REP(i,1,n)
#define rep0(i,n) REP(i,0,n-1)
#define repG(i,x) for(ll i=pos[x];~i;i=e[i].next)
#define ll long long
#define db double
const ll N=3e5+7;
const ll INF=1e9+7;
ll T,n,num,cnt,Ans=0;
ll pos[N],sz[N],top[N],fa[N],son[N],ind[N],v[N],w[N],mx[N],nx[N],ns[N],dw[N],p[N],dep[N];
struct edge{ll v,next;}e[N*2];
void add(ll x,ll y){e[num]=(edge){y,pos[x]}; pos[x]=num++;}
void dfs1(ll x,ll f,ll d){
dep[x]=d;
fa[x]=f;
sz[x]=1;
mx[x]=ns[x]=0;
repG(i,x){
if(e[i].v==f)continue;
dfs1(e[i].v,x,d+1);
sz[x]+=sz[e[i].v];
if(sz[e[i].v]>mx[x]){
ns[x]=son[x];
nx[x]=mx[x];
mx[x]=sz[e[i].v];
son[x]=e[i].v;
}
else if(sz[e[i].v]>nx[x]){
nx[x]=sz[e[i].v];
ns[x]=e[i].v;
}
}
}
void dfs2(ll x,ll tp){
top[x]=tp;
v[++cnt]=n-sz[x];
ind[x]=cnt;
w[cnt]=x;
dw[x]=x;
if(sz[x]==1)return;
dfs2(son[x],tp);
dw[x]=dw[son[x]];
repG(i,x)if(e[i].v!=son[x]&&e[i].v!=fa[x])dfs2(e[i].v,e[i].v);
}
void dfs3(ll x){
if(x!=1){
ll u=fa[x],k,ks;
while(top[u]!=1)u=fa[top[u]];
ll t=upper_bound(v+1,v+ind[u]+1,n-((n+sz[x]+1)/2))-v-1;
ll q=w[t];
if(son[q]==x||q!=u){
if(2*nx[q]<n-sz[x]){
k=q;
ks=sz[q]-sz[x];
}
else {
ll tt=upper_bound(v+ind[ns[q]],v+ind[dw[ns[q]]]+1,n-(n-sz[x]+1)/2)-v-1;
k=w[tt];
ks=sz[k];
}
}
else{
if(2*mx[q]<n-sz[x]){
k=q;
ks=sz[q]-sz[x];
}
else{
ll tt=upper_bound(v+ind[u]+1,v+ind[dw[u]]+1,n-(n-sz[x]+1)/2)-v-1;
k=w[tt];
ks=sz[k];
}
}
Ans+=k;
if(ks*2==n-sz[x])Ans+=fa[k];
}
if(sz[x]==1){
p[x]=x;
Ans+=x;
return;
}
repG(i,x)if(e[i].v!=fa[x])dfs3(e[i].v);
if(x!=1){
p[x]=p[son[x]];
while(sz[p[x]]*2<sz[x])p[x]=fa[p[x]];
if(sz[p[x]]*2==sz[x])Ans+=fa[p[x]];
Ans+=p[x];
}
}
int main(){
scanf("%lld",&T);
rep(o,T){
num=cnt=Ans=0;
memset(pos,-1,sizeof(pos));
scanf("%lld",&n);
rep(i,n-1){
ll x,y; scanf("%lld%lld",&x,&y);
add(x,y); add(y,x);
}
dfs1(1,0,1);
dfs2(1,1);
dfs3(1);
printf("%lld\n",Ans);
}
return 0;
}