震惊,我居然不会数细胞。
菜的离谱。
T1: 100 ( ightarrow) 80
T4: 20 ( ightarrow) 0
T1:松鼠的新家
震惊,我建边居然不开二倍,那没事了。
树上差分裸题。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+10;
int n;
int a[maxn];
struct Edge{
int from,to,nxt;
}e[maxn];//这样直接80 = =
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int head[maxn],cnt;
inline int add(int u,int v){
e[++cnt].from=u;
e[cnt].to=v;
e[cnt].nxt=head[u];
head[u]=cnt;
}
int fa[maxn],dep[maxn],siz[maxn],son[maxn];
void dfs1(int u){
dep[u]=dep[fa[u]]+1;siz[u]=1;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
fa[v]=u;dfs1(v);
siz[u]+=siz[v];
if(!son[u]||siz[v]>siz[son[u]])son[u]=v;
}
}
int top[maxn];
void dfs2(int u,int t){
top[u]=t;
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
dfs2(v,v==son[u]?t:v);
}
}
inline int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]])swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int c[maxn];
long long f[maxn];
void dfs3(int u){
f[u]=c[u];
for(int i=head[u];i;i=e[i].nxt){
int v=e[i].to;
if(v==fa[u])continue;
dfs3(v);
f[u]+=f[v];
}
}
int main(){
#ifndef LOCAL
freopen("home.in","r",stdin);
freopen("home.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
add(u,v);add(v,u);
}
dfs1(1);dfs2(1,1);
for(int i=1;i<n;i++){
int u=a[i],v=a[i+1],lcam=lca(u,v);
c[u]++;c[v]++;c[lcam]--;
if(fa[lcam])c[fa[lcam]]--;
}
dfs3(1);
for(int i=2;i<=n;i++)
f[a[i]]--;
for(int i=1;i<=n;i++)
printf("%lld
",f[i]);
return 0;
}
谢谢,已经在学了
T2:trade
震惊,贪心题我居然 DP,直接挂掉。
震惊,第二维限制到 500 之后直接 AC。
震惊,天皇震怒直接卡掉
乱搞的 Code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n;
int a[maxn];
int f[2][maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int main(){
#ifndef LOCAL
freopen("trade.in","r",stdin);
freopen("trade.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++)
a[i]=read();
memset(f,-0x3f,sizeof(f));
f[0][0]=0;
int k=0;
for(register int i=1;i<=n;i++){
k^=1;
for(register int j=0;j<min(i,500);j++){
f[k][j]=max(f[k][j],f[k^1][j]);
f[k][j+1]=max(f[k][j+1],f[k^1][j]-a[i]);
if(j)f[k][j-1]=max(f[k][j-1],f[k^1][j]+a[i]);
}
}
printf("%d
",f[k][0]);
return 0;
}
所以正解居然是反悔贪心。
可能大家都能想到贪心,那就是从后选最大的,从前选最小的。那么如何维护?
用一个小根堆来维护所有的 (a[i]),如果队列为空或当前的 (a[i]) 小于等于堆顶,直接入队。否则,买入堆顶并在现在卖出,弹出堆顶,压入当前的两个货物。
由于我们使用小根堆,所以当前堆顶一定是之前最小的买入价值。所以我们买入堆顶的策略肯定是最优的。但在今天卖出却不一定是最优的,因为之后可能出现更大的 (a[i])。所以我们要用可撤销贪心。
假设我们之后会用一次撤销操作,那么设堆顶的货物价值为 (a),今天卖出的价值为 (b),之后最优日卖出的价值为 (c)。因为我们之前取了 (b-a) 作价值,然而事实上最优策略是 (c-a)。所以我们只需要再取一个 (c-b) 作价值即可。这就是压入的第一个货物。至于第二个货物,由于实际上当前策略由 (b-a) 转换成了 (c-a) 了,所以在价值为 (b) 的那天就不必卖东西了,那么就可以选择在当日购入物品了,第二个货物就对应这个物品。
正确的 Code
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n;
long long ans;
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
priority_queue<int,vector<int>,greater<int> > q;
int main(){
#ifndef LOCAL
freopen("trade.in","r",stdin);
freopen("trade.out","w",stdout);
#endif
n=read();
for(int i=1;i<=n;i++){
int a=read();
if(q.empty()||a<=q.top())q.push(a);
else{
ans+=a-q.top();q.pop();
q.push(a);q.push(a);
}
}
printf("%lld
",ans);
return 0;
}
T3:sum
震惊,这题居然是莫队。
其实前面的两个特殊性质都是提示。既然要用莫队,我们就看 (S_n^m) 如何转移。
当 (n) 相等时,显然:
当 (m) 相等时,我们可以画一个杨辉三角。例如:
1 3 3
1 4 6
设下面一行就是第 (n) 行,上面一行是 (n-1) 行,我们都取 (m) 个。那么依据小学知识可知 1=1
,4=1+3
,6=3+3
。我们发现,在 (n-1) 行转移到 (n) 行的过程中,除了第 (m) 项,其他的都转移了两次。那么我们显然可以得到:
由于题目可离线,利用这四个 (O(1)) 转移的式子,我们可以利用莫队解决问题。注意由于 (ngeq m) ,所以 (m) 为左端点,(n) 为右端点(震惊,好像反过来也可以)。块长取 (sqrt{max n}) 。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int Mod=1e9+7;
const int maxn=1e5+10;
int Max,T,S,ans=1;//ans别初始化成0了...
int res[maxn];
struct Node{
int n,m,id;
friend inline bool operator <(register const Node& A,register const Node& B){
return (A.m/S)^(B.m/S)?A.m<B.m:(((A.m/S)&1)?A.n<B.n:A.n>B.n);
}
}q[maxn];
inline int read(){
int x=0;bool fopt=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
return fopt?x:-x;
}
int fac[maxn],inv[maxn];
inline void Init(){
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=1e5;i++)
inv[i]=(Mod-Mod/i)*inv[Mod%i]%Mod;
for(int i=2;i<=1e5;i++){
fac[i]=fac[i-1]*i%Mod;
inv[i]=inv[i-1]*inv[i]%Mod;
}
}
inline int C(int n,int m){
if(n<m)return 0;
if(!m||n==m)return 1;
return fac[n]*inv[m]%Mod*inv[n-m]%Mod;
}
signed main(){
#ifndef LOCAL
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
#endif
read();Init();
T=read();
for(int i=1;i<=T;i++){
int n=read(),m=read();
if(n>Max)Max=n;
q[i]=(Node){n,m,i};
}
S=sqrt(Max);
sort(q+1,q+T+1);
int m=1,n=0;//莫队左端点建议从1开始,否则有的题会挂,比如小Z的袜子
for(int i=1;i<=T;i++){
int nowm=q[i].m,nown=q[i].n;
while(n<nown)ans=(2*ans%Mod-C(n++,m)+Mod)%Mod;
while(m>nowm)ans=(ans-C(n,m--)+Mod)%Mod;
while(n>nown)ans=(ans+C(--n,m))%Mod*inv[2]%Mod;
while(m<nowm)ans=(ans+C(n,++m))%Mod;
res[q[i].id]=ans;
}
for(int i=1;i<=T;i++)
printf("%lld
",res[i]);
return 0;
}
T4:building
震惊,我居然不会数细胞。已经开始学队列和栈了。
神奇模拟,正在努力。