NOI前的考试日志
4.14 网络流专项测试
先看T1,不会,看T2,仙人掌???wtf??弃疗。看T3,貌似最可做了,然后开始刚,刚了30min无果,打了50分暴力,然后接着去看T1,把序列差分了一下,推了会式子,发现是傻逼费用流,然后码码码,码完秒过大样例,觉得比较稳,又肉眼查了会错,就放了。然后接着推T3,发现我会做一个限制条件的,貌似和T1差不多,然后就写了,感觉能多骗点分,之后看了看T2,发现30裸树剖,30裸最大流,然后码码码。最后查了会错,发现T1没开long long,赶紧改了。100+44+63=207 rank1/5。T2挂了12分,不知道为什么,真的是静态仙人掌裸题,T3比较巧妙。
T1,差分之后每个本来就满足条件的位置有一个可以贡献的流量从S连过来,不满足条件的位置有一个需要的流量连向T。然后每种操作可以连接两个距离为l的位置,然后直接最小费用最大流即可,判断无解可以看最大流是不是等于我们需要的流量。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <deque>
#define inf 0x3fffffff
#define N 250
using namespace std;
int e=,head[N];
struct edge{
int u,v,f,w,next;
}ed[N*N<<];
void add(int u,int v,int f,int w){
ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;ed[e].w=-w;
ed[e].next=head[v];head[v]=e++;
}
int dis[N],pre[N],bo[N],tim,S,T;
int sum,n,m,a[N],b[N];
bool spfa(){
memset(dis,0x3f,sizeof dis);
tim++;
deque<int> q;q.push_front(S);dis[S]=;
while(!q.empty()){
int x=q.front();q.pop_front();bo[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]>dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;pre[v]=i;
if(bo[v]!=tim){
bo[v]=tim;
if(!q.empty()&&dis[v]<dis[q.front()])q.push_front(v);
else q.push_back(v);
}
}
}
}
return dis[T]!=dis[T+];
}
long long ans=;
int dfs(int x,int f){
bo[x]=tim;
int now=;
if(x==T){
ans+=1ll*f*dis[T];
return f;
}
for(int i=head[x];i;i=ed[i].next){
if(ed[i].f&&dis[ed[i].v]==dis[x]+ed[i].w&&bo[ed[i].v]!=tim){
int nxt=dfs(ed[i].v,min(f,ed[i].f));
now+=nxt;f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
return now;
}
void work(){
int flow=;
while(spfa()){
do{
tim++;
flow+=dfs(S,inf);
}while(bo[T]==tim);
}
if(flow!=sum)puts("-1");
else printf("%lld\n",ans);
}
int main(){
// freopen("test.in","r",stdin);
scanf("%d%d",&n,&m);
S=n+;T=S+;
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<n;i++){
b[i]=a[i+]-a[i];
if(b[i]>)add(S,i,b[i],);
else add(i,T,-b[i],),sum-=b[i];
}
add(S,,inf,);add(S,n,inf,);
char o[];
for(int i=,l,c;i<=m;i++){
scanf("%s%d%d",o,&l,&c);
for(int j=;j+l<=n;j++){
if(o[]=='+')add(j+l,j,inf,c);
else add(j,j+l,inf,c);
}
}
work();
return ;
}
T2,静态仙人掌裸题,咕咕咕。
T3,比较巧妙的一个费用流。我们先假设将所有的零件都放进去,这样可能会出现不合法的情况,我们还是考虑枚举,这里我们枚举每行/列的限制,然后我们将对应行和列之间连上(k,0)的边,代表最多有这么多不被删去,剩下的如果位置(i,j)是合法的,则从i行想j列连一条(1,1)的边,代表这个零件被删去了。然后每次我们跑的都是最小费用最大流,只需要判断是否满流以及最后的零件总数是不是满足条件。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <deque>
#define N 88
#define inf 0x3fffffff
using namespace std;
int e,head[N];
struct edge{
int u,v,f,w,next;
}ed[N*N];
void add(int u,int v,int f,int w){
ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;ed[e].w=-w;
ed[e].next=head[v];head[v]=e++;
}
char s[N][N];
int n,A,B,S,T,ans=-;
int dis[N],pre[N],bo[N];
int a[N],b[N],tot,lim,num1;
bool spfa(){
memset(dis,0x3f,sizeof dis);
memset(bo,,sizeof bo);
deque<int> q;q.push_front(S);dis[S]=;
while(!q.empty()){
int x=q.front();q.pop_front();bo[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]>dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;pre[v]=i;
if(!bo[v]){
bo[v]=;
if(!q.empty()&&dis[v]<dis[q.front()])q.push_front(v);
else q.push_back(v);
}
}
}
}
return dis[T]!=dis[T+];
}
int Flow,Cost;
int dfs(int x,int f){
bo[x]=;
if(x==T){
Flow+=f;
Cost+=dis[T]*f;
return f;
}
int ans=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]==dis[x]+ed[i].w&&!bo[v]){
int nxt=dfs(v,min(f,ed[i].f));
ans+=nxt;f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
return ans;
}
void work(){
Flow=,Cost=;
while(spfa()){
do{
memset(bo,,sizeof bo);
dfs(S,inf);
}while(bo[T]);
}
if(Flow==tot&&lim*B<=(tot-Cost)*A)
ans=max(ans,tot-Cost);
}
int tim;
int main(){
while(scanf("%d%d%d",&n,&A,&B)==&&((n+A+B)!=)){
num1=;tot=;
memset(a,,sizeof a);
memset(b,,sizeof b);
for(int i=;i<=n;i++){
scanf("%s",s[i]+);
for(int j=;j<=n;j++){
if(s[i][j]!='/'){
tot++;a[i]++;b[j]++;
if(s[i][j]=='C')num1++;
}
}
}
S=n*+;T=S+;ans=-;
for(lim=;lim<=n;lim++){
e=;memset(head,,sizeof head);
for(int i=;i<=n;i++){
add(S,i,a[i],);
add(n+i,T,b[i],);
add(i,n+i,lim,);
for(int j=;j<=n;j++)if(s[i][j]=='.')
add(i,n+j,,);
}
work();
}
printf("Case %d: ",++tim);
if(ans==-)puts("impossible");
else printf("%d\n",ans-num1);
}
return ;
}
裸的spfa在bzoj上T,在wangxh的建议下改成了多路增广,然后跑的飞快!
4.17
开场看T1,貌似不是很难,但是yy了一会,感觉O(n^2)暴力有点难写,于是去看T2,发现是二分图博弈裸题,然后码,二分图和dinic拍,本来以为二分图过不了呢,结果跑的都很快。之后看T3,这不是Cooook讲的原题嘛,操,我不会SAM,因为记得他当时说SA不能做,就没想,写了个30分kmp暴力,之后又去yyT1,然后yy出了那个O(n^2)的暴力,然后写,写完和状压拍,拍上了,然后想优化,发现我这些操作就是一个线段树,然后赶紧码上,拍上了,然后感觉今天这题岂不是会有很多人AK,然而我挂在了一道原题上。。之后又静态查了波错就完了。100+100+30=230 rank2/10,没有挂分,不错,没有人AK??有点不可思议。
T1,f[i][j]表示前i个位置,第i个位置水的高度是j的答案,cnt[i][j]=i这个位置j高度的水能满足多少条件。
转移的话:$$f[i][j]=Max_{k=0}^{h[i]} (f[i-1][k]+cnt[i][j]) (j<=h[i])$$
$$f[i][j]=f[i-1][j]+cnt[i][j] (j>h[i])$$
然后这个加法和去Max都可以通过线段树来操作,就可以O(nlogn)了,貌似正解是树形dp??
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 100050
using namespace std;
int T,n,m,ans,h[N],num[N<<],num_cnt;
struct data{int x,y,o;}d[N];
inline bool cmp(data a,data b){
if(a.x==b.x)return a.y<b.y;
return a.x<b.x;
}
int maxn[N<<],tag[N<<],lazy[N<<];
inline void pushdown(int rt){
if(tag[rt]){
maxn[rt<<]=tag[rt<<]=tag[rt];
lazy[rt<<]=;
maxn[rt<<|]=tag[rt<<|]=tag[rt];
lazy[rt<<|]=;
tag[rt]=;
}
if(lazy[rt]){
maxn[rt<<]+=lazy[rt];
lazy[rt<<]+=lazy[rt];
maxn[rt<<|]+=lazy[rt];
lazy[rt<<|]+=lazy[rt];
lazy[rt]=;
}
}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){
maxn[rt]+=z;lazy[rt]+=z;
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
maxn[rt]=max(maxn[rt<<],maxn[rt<<|]);
}
void change(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){
maxn[rt]=tag[rt]=z;lazy[rt]=;
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)change(rt<<,l,mid,x,y,z);
if(y>mid)change(rt<<|,mid+,r,x,y,z);
maxn[rt]=max(maxn[rt<<],maxn[rt<<|]);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return maxn[rt];
pushdown(rt);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return max(query(rt<<,l,mid,x,y),query(rt<<|,mid+,r,x,y));
}
inline int read(){
int a=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<=''){a=a*+(ch^);ch=getchar();}
return a;
}
int main(){
register int i,l,r,mx;
T=read();
while(T--){
n=read();m=read();
num_cnt=;
for(i=;i<n;++i){
h[i]=read();
num[++num_cnt]=h[i];
}
for(i=;i<=m;++i){
d[i].x=read();d[i].y=read();d[i].o=read();
num[++num_cnt]=++d[i].y;
}
sort(d+,d+m+,cmp);
sort(num+,num+num_cnt+);
num_cnt=unique(num+,num+num_cnt+)-num-;
for(i=;i<n;++i)
h[i]=lower_bound(num+,num+num_cnt+,h[i])-num;
for(i=;i<=m;++i)
d[i].y=lower_bound(num+,num+num_cnt+,d[i].y)-num;
memset(maxn,,sizeof maxn);
memset(tag,,sizeof tag);
memset(lazy,,sizeof lazy);
for(i=,l=,r=;i<=n;++i){
while(r<m&&d[r+].x==i)++r;
for(;l<=r;++l){
if(d[l].o)update(,,num_cnt,d[l].y,num_cnt,);
else update(,,num_cnt,,d[l].y-,);
}
mx=query(,,num_cnt,,h[i]);
change(,,num_cnt,,h[i],mx);
}
printf("%d\n",maxn[]);
}
}
T2,裸的二分图博弈,不解释。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define N 10050
#define inf 0x3fffffff
using namespace std;
int e=,head[N];
struct edge{
int u,v,next;
}ed[N<<];
void add(int u,int v){
ed[e].u=u;ed[e].v=v;
ed[e].next=head[u];head[u]=e++;
}
char s[];
int n,m,can[][],id[][],tot1,tot2,cnt;
int ans[N],bo[N],pp[N];
bool find(int x){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(!bo[v]){
bo[v]=;
if(!pp[v]||find(pp[v])){
pp[v]=x;
pp[x]=v;
return ;
}
}
}
return ;
}
void dfs(int x,int o){
bo[x]=;
// printf("x====%d %d\n",x,o);
if(!o){
ans[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(!bo[v])dfs(v,o^);
}
}
else dfs(pp[x],o^);
}
int main(){
// freopen("test.in","r",stdin);
// freopen("2.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%s",s+);
for(int j=;j<=m;j++)if(s[j]=='.'){
can[i][j]=;
if((i+j)&)id[i][j]=++tot1;
else id[i][j]=++tot2;
}
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)if(can[i][j]){
if(!((i+j)&))id[i][j]+=tot1;
// printf("%d %d %d\n",i,j,id[i][j]);
} for(int i=;i<=n;i++)
for(int j=;j<m;j++)if(can[i][j]&&can[i][j+]){
add(id[i][j],id[i][j+]);
add(id[i][j+],id[i][j]);
}
for(int i=;i<n;i++)
for(int j=;j<=m;j++)if(can[i][j]&&can[i+][j]){
add(id[i][j],id[i+][j]);
add(id[i+][j],id[i][j]);
} for(int i=;i<=tot1;i++){
memset(bo,,sizeof bo);
find(i);
}
for(int i=;i<=tot1+tot2;i++)if(!pp[i]){
memset(bo,,sizeof bo);
dfs(i,);
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)if(can[i][j]&&ans[id[i][j]])cnt++;
printf("%d\n",cnt);
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)if(can[i][j]&&ans[id[i][j]])printf("%d %d\n",i,j);
return ;
}
T3,后缀自动机,分类讨论。当$k>\sqrt{n}$时,我们暴力跑,时间复杂度O(mqlogn)。否则我们对于每个串暴力O(k^2)在后缀自动机上跑,对于每个区间的贡献,离线用莫队来维护,时间复杂度$O(m\sqrt{m}+qk^{2})$。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define LL long long
#define N 100500
using namespace std;
int n,m,q,K,last,tot,l[N],r[N];
char s[N];
int ch[N<<][],mx[N<<],par[N<<],size[N<<];
int e=,head[N<<];
struct edge{
int u,v,next;
}ed[N<<];
void add(int u,int v){
ed[e].u=u;ed[e].v=v;
ed[e].next=head[u];head[u]=e++;
}
void extend(int c,int o){
int p=last;
if(ch[p][c]){
int q=ch[p][c];
if(mx[q]==mx[p]+){last=q;return ;}
int nq=++tot;par[nq]=par[q];mx[nq]=mx[p]+;
memcpy(ch[nq],ch[q],sizeof ch[nq]);
for(;p&&ch[p][c]==q;p=par[p])ch[p][c]=nq;
par[q]=nq;
last=nq;return;
}
int np=++tot;mx[np]=mx[p]+;size[np]=o;
for(;p&&!ch[p][c];p=par[p])ch[p][c]=np;
if(!p)par[np]=;
else{
int q=ch[p][c];
if(mx[q]==mx[p]+)par[np]=q;
else{
int nq=++tot;par[nq]=par[q];mx[nq]=mx[p]+;
memcpy(ch[nq],ch[q],sizeof ch[nq]);
for(;p&&ch[p][c]==q;p=par[p])ch[p][c]=nq;
par[q]=par[np]=nq;
}
}
last=np;
}
int a[N],b[N];
namespace work1{
char t[N];
int fa[N<<][];
LL ans;
vector <int> pp[];
void dfs(int x,int d){
for(int i=;(<<i)<=d;i++)
fa[x][i]=fa[fa[x][i-]][i-];
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
fa[v][]=x;dfs(v,d+);
size[x]+=size[v];
}
}
int find(int x,int l){
for(int i=;~i;i--)
if(mx[fa[x][i]]>=l)x=fa[x][i];
return x;
}
void Main(){
for(int i=;i<=q;i++){
scanf("%s%d%d",t+,&a[i],&b[i]);
a[i]++;b[i]++;
last=;
pp[i].push_back(last);
for(int j=;j<=K;j++){
extend(t[j]-'a',);
pp[i].push_back(last);
}
}
for(int i=;i<=tot;i++)add(par[i],i);
dfs(,);
for(int i=;i<=q;i++){
ans=;
for(int j=a[i];j<=b[i];j++)
ans+=size[find(pp[i][r[j]],r[j]-l[j]+)];
printf("%lld\n",ans);
}
return ;
}
}
namespace work2{
int be[N];
int cnt[][];
LL ans[N];
struct data{
char t[];
int a,b,id;
}d[N];
bool cmp(data a,data b){
if(be[a.a]==be[b.a])return a.b<b.b;
return be[a.a]<be[b.a];
}
LL query(data x){
LL ans=;
for(int i=;i<=K;i++){
for(int j=i,now=;j<=K;j++){
if(!ch[now][x.t[j]-'a'])break;
now=ch[now][x.t[j]-'a'];
ans+=1ll*size[now]*cnt[i][j];
}
}
return ans;
}
void dfs(int x){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
dfs(v);
size[x]+=size[v];
}
}
void Main(){
for(int i=;i<=tot;i++)add(par[i],i);
dfs();
for(int i=;i<=q;i++){
d[i].id=i;
scanf("%s%d%d",d[i].t+,&d[i].a,&d[i].b);
d[i].a++;d[i].b++;
}
int mm=sqrt(m);
for(int i=;i<=m;i++)be[i]=(i-)/mm+;
sort(d+,d+q+,cmp);
int posl=,posr=;
for(int i=;i<=q;i++){
while(posl<d[i].a)cnt[l[posl]][r[posl]]--,posl++;
while(posl>d[i].a)posl--,cnt[l[posl]][r[posl]]++;
while(posr<d[i].b)posr++,cnt[l[posr]][r[posr]]++;
while(posr>d[i].b)cnt[l[posr]][r[posr]]--,posr--;
ans[d[i].id]=query(d[i]);
}
for(int i=;i<=q;i++)printf("%lld\n",ans[i]);
}
}
int main(){
scanf("%d%d%d%d",&n,&m,&q,&K);
scanf("%s",s+);
last=++tot;
for(int i=;i<=n;i++)extend(s[i]-'a',);
for(int i=;i<=m;i++)scanf("%d%d",&l[i],&r[i]),l[i]++,r[i]++;
if(K>sqrt(n))work1::Main();
else work2::Main();
return ;
}
4.19
开场看T1,发现和原来做的商店购物有点像,估计是一堆组合数乱搞,先放放。看T2,裸fwt啊!裸的fwt有五十分,然后发现现在要求$x+x^{2}+x^{4}+x^{8}....$,然后我玄学的乱猜了个结论,这个会有循环节,然后就打出来了,发现好像是对的。就去看T3,只会30分暴力,之后一直在想从谁走到谁会做出多少贡献,发现概率不同,贡献也不同,之后赶紧去把T1的50分暴力打上了,然后想到了容斥,没时间打了。GG。40+100+30=170 rank1/10。T1up没有减一挂了10分。
T1,容斥前几个有几个不合法,然后直接组合数,组合数要用exLucas,然后我发现我原来学了一个假的exLucas,非常优秀。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int read(){
int a=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<=''){a=a*+(ch^);ch=getchar();}
return a;
}
int T,mod,n,n1,n2,m,ans,up[];
int cnt[],p[],pk[],K[],r[],num,fac[][];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b,int c){
int ans=;
while(b){
if(b&)ans=1ll*ans*a%c;
a=1ll*a*a%c; b>>=;
}
return ans;
}
#define pr pair<int,int>
#define fir first
#define sec second
pr calc(int n,int x){
if(!n)return pr(,);
int a=qp(fac[x][pk[x]-],n/pk[x],pk[x]);
int b=n/p[x];
pr ans=calc(n/p[x],x);
a=1ll*a*fac[x][n%pk[x]]%mod;
ans.fir=1ll*ans.fir*a%pk[x];
ans.sec+=b;
return ans;
}
int C(int n,int m,int x){
if(m>n||m<)return ;
if(m==n||m==)return ;
pr ans1=calc(n,x),ans2=calc(m,x),ans3=calc(n-m,x);
int a=1ll*ans1.fir
*qp(ans2.fir,pk[x]/p[x]*(p[x]-)-,pk[x])%pk[x]
*qp(ans3.fir,pk[x]/p[x]*(p[x]-)-,pk[x])%pk[x];
int b=ans1.sec-ans2.sec-ans3.sec;
if(b>=K[x])return ;
return 1ll*a*qp(p[x],b,pk[x])%pk[x];
}
int crt(){
int ans=;
for(int i=,tmp;i<=num;i++){
tmp=qp(mod/pk[i],pk[i]/p[i]*(p[i]-)-,pk[i]);
UPD(ans,1ll*(mod/pk[i])*tmp%mod*r[i]%mod);
}
return ans;
}
int getc(int n,int m){
if(m>n||m<)return ;
if(m==n||m==)return ;
for(int i=;i<=num;i++){
r[i]=C(n,m,i);
}
return crt();
}
int main(){
register int i,j,sum,now;
T=read();mod=read();
if(mod==)num=,p[]=pk[]=,K[]=;
if(mod==){
p[]=pk[]=,p[]=pk[]=,p[]=pk[]=,p[]=pk[]=,p[]=pk[]=;
K[]=K[]=K[]=K[]=K[]=;num=;
}
if(mod==){
num=K[]=K[]=;K[]=;
p[]=;pk[]=;p[]=;pk[]=;p[]=;pk[]=;
}
for(i=;i<=num;i++){
fac[i][]=;
for(int j=;j<pk[i];j++)
if(j%p[i])fac[i][j]=1ll*fac[i][j-]*j%pk[i];
else fac[i][j]=fac[i][j-];
}
while(T--){
ans=;
n=read();n1=read();n2=read();m=read()-n;
for(i=;i<=n1;++i)up[i]=read();
for(i=n1+;i<=n1+n2;++i)m-=read()-;
for(i=;i<=n1;++i)up[i]--;
for(i=;i<(<<n1);i++){
cnt[i]=cnt[i>>]+(i&);
for(j=,sum=;j<=n1;j++)if(i&(<<j-))
sum+=up[j]+;
now=getc(m-sum+n-,n-);
if(cnt[i]&)ans=(ans-now+mod)%mod;
else UPD(ans,now);
}
printf("%d\n",ans);
}
return ;
}
T2,考场打的循环节其实是有理论依据的,我们现在其实是要求最小的x满足$2^{x}=2 (mod \; \phi{p})$,然后我们会发现这个x等于5003,其实这里用到了ex欧拉定理,其实也是因为2比较特殊。更优秀一点的做法是倍增求解,我们需要倍增处理出$x^{2^{2^{a}}}$和$\sum_{i=0}^{a}{x^{2^{2^{i}}}}$,然后直接上fwt就可以了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <ctime>
#define N 277777
#define mod 10007
using namespace std;
int a[N],n,m,p,ans,all;
int inv2=;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
void fwt(int *a){
register int i,j,k,x,y;
for(k=;k<=all;k<<=)
for(i=;i<all;i+=k)
for(j=;j<(k>>);j++){
x=a[i+j],y=a[i+j+(k>>)];
a[i+j]=(x+y)%mod;
a[i+j+(k>>)]=(x-y+mod)%mod;
}
}
void ifwt(int *a){
register int i,j,k,x,y;
for(k=;k<=all;k<<=)
for(i=;i<all;i+=k)
for(j=;j<(k>>);j++){
x=a[i+j],y=a[i+j+(k>>)];
a[i+j]=(x+y)*inv2%mod;
a[i+j+(k>>)]=(x-y+mod)*inv2%mod;
}
}
int sum[mod+],vis[mod+],tim[mod+];
int s1[mod+],pos[mod+],val[mod+],len[mod+],s2[mod+];
int pp[mod+];
void work(){
int now,cnt;
for(int i=;i<mod;i++){
now=i;cnt=;
sum[cnt]=now;
vis[now]=i;
tim[now]=cnt;
while(vis[now*now%mod]!=i){
now=now*now%mod;cnt++;
sum[cnt]=(sum[cnt-]+now)%mod;
vis[now]=i;tim[now]=cnt;
}
now=now*now%mod;
s1[i]=sum[tim[now]-];
pos[i]=tim[now];
val[i]=now;
len[i]=cnt-tim[now]+;
s2[i]=(sum[cnt]-sum[tim[now]-]+mod)%mod;
}
m++;
for(int i=;i<mod;i++){
if(m<pos[i]){
now=i;
for(int j=;j<=m;j++){
UPD(pp[i],now);
now=now*now%mod;
}
}
else{
pp[i]=s1[i];
int l=(m-pos[i]+);
UPD(pp[i],(l/len[i])%mod*s2[i]%mod);
l%=len[i];now=val[i];
for(int j=;j<=l;j++){
UPD(pp[i],now);
now=now*now%mod;
}
}
}
for(int i=;i<all;i++)a[i]=pp[a[i]];
}
int main(){
scanf("%d%d%d",&n,&m,&p);
all=(<<n);
for(int i=;i<all;i++)scanf("%d",&a[i]);
fwt(a);work();ifwt(a);
printf("%d\n",a[p]);
}
T3,我们考虑把环拆成一个序列,然后我们dp时就是钦定该段只剩最后一个元素是点,然后求出从初始状态到该状态的期望和概率,转移时再枚举剩下的点中谁是最后一个变成X的,然后就变成了我们已知的f值,这样区间dp下去就可以了。
具体的转移方程:f表概率,g表期望,p表示l~r这段除r外最后变成X的.的位置是k的概率
$$p(l,r,k)={\frac{len(l,k)}{len(l,r)}}^{num(l,k)} {\frac{len(k+1,r)}{len(l,r)}}^{num(k+1,r)-1}*C_{num(l,r)-2}^{num(l,k)-1}g(l,k)g(k+1,r)$$
$$g(l,r)=\sum_{k=l}^{r-1}{p(l,r,k)}$$
$$f(l,r)=\frac{1}{g(l,r)} \sum_{k=l}^{r-1}{p(l,r,k)(f(l,k)+f(k+1,r)+\frac{k-i}{2})}$$
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 205
#define nxt(x) (((x)==n)?(1):((x)+1))
#define pre(x) (((x)==1)?(n):((x)-1))
using namespace std;
char s[N];
int visf[N][N],visg[N][N],len[N][N],cnt[N][N],n,T;
double f[N][N],g[N][N],C[N][N],pw[N][N],ans;
double P(int l,int r,int k);
double G(int l,int r);
double F(int l,int r);
double P(int l,int r,int k){
if(s[r]=='X'||s[k]=='X')return ;
int c1=cnt[l][k]-,c2=cnt[nxt(k)][r]-;
return C[c1+c2][c1]*pw[len[l][k]][c1+]*pw[len[nxt(k)][r]][c2]/pw[len[l][r]][c1+c2+]*G(l,k)*G(nxt(k),r);
}
double G(int l,int r){
if(visg[l][r])return g[l][r];
if(s[r]=='X')return ;
if(cnt[l][r]==)return ;
double ans=;
for(int i=l;i!=r;i=nxt(i))
ans+=P(l,r,i);
visg[l][r]=;
return g[l][r]=ans;
}
double F(int l,int r){
if(visf[l][r])return f[l][r];
if(s[r]=='X')return ;
if(cnt[l][r]==)return ;
if(G(l,r)<1e-)return ;
double ans=;
for(int i=l;i!=r;i=nxt(i))
ans+=P(l,r,i)*(F(l,i)+F(nxt(i),r)+(len[l][i]-)/2.0);
visf[l][r]=;
return f[l][r]=ans/G(l,r);
}
int main(){
scanf("%d",&T);
for(int i=;i<=;i++){
C[i][]=;
for(int j=;j<=i;j++)
C[i][j]=C[i-][j-]+C[i-][j];
}
for(int i=;i<=;i++){
pw[i][]=;
for(int j=;j<=;j++)
pw[i][j]=pw[i][j-]*i;
}
while(T--){
scanf("%s",s+);
n=strlen(s+);
memset(cnt,,sizeof cnt);
for(int i=;i<=n;i++){
cnt[i][i]=(s[i]=='.');
len[i][i]=;
for(int j=nxt(i);j!=i;j=nxt(j))
cnt[i][j]=cnt[i][pre(j)]+(s[j]=='.'),
len[i][j]=len[i][pre(j)]+;
}
if(!cnt[][n]){puts("");continue;}
memset(visf,,sizeof visf);
memset(visg,,sizeof visg);
ans=;
for(int i=;i<=n;i++)
ans+=G(nxt(i),i)*F(nxt(i),i);
printf("%0.8f\n",ans+(n-)/2.0);
}
return ;
}
4.22
怎么又考试啊。先看T1,35分sb杜教筛phi,20分暴力,看起来很好,感觉又是组合数乱搞?看T2,只会20分裸暴力,yy了一会循环节,但是觉得至少是O(p)的,就没深入想,然后看T3,多项式?卷积?不错,这不是裸的多点求值嘛!yy了一会,不会,但是感觉这肯定不是正解,然后又乱yy了一会,啥也不会,然后哪道题都不会了,就GG了。40+0+30=70 rank10/10,T1傻逼暴力挂了,T2矩阵中我的-1没有+mod爆负了,然后就挂了。
T1,容斥最后gcd的质因子,$$\sum_{i=1}^{n}{\mu{(i)} \cdot C_{n/i+k-1}^{k}}$$需要杜教筛mu,然后除法分块。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
#define mod 1000000007
#define N 10000050
using namespace std;
int tot,mu[N],sm[N],prime[],T,n,m,ans;
int inv[],fac[],Inv;
bool vis[N];
void init(){
mu[]=sm[]=;
for(int i=;i<=;i++){
if(!vis[i]){
prime[++tot]=i;
mu[i]=mod-;
}
for(int j=;j<=tot&&i*prime[j]<=;j++){
vis[i*prime[j]]=;
if(i%prime[j]==){
mu[i*prime[j]]=;
break;
}
mu[i*prime[j]]=mod-mu[i];
}
sm[i]=(sm[i-]+mu[i])%mod;
}
}
map<int,int> mm;
int gets(int n){
if(n<=)return sm[n];
if(mm.count(n))return mm[n];
int ans=;
for(int i=,j;i<=n;i=j+){
j=n/(n/i);
ans=(ans-1ll*(j-i+)*gets(n/i)%mod+mod)%mod;
}
return mm[n]=ans;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n){
int ans=Inv;
if(n<=)ans=1ll*ans*fac[n]%mod;
else for(int i=;i<=m;i++)ans=1ll*ans*(n-i+)%mod;
return ans;
}
int main(){
//freopen("test.in","r",stdin);
init();
inv[]=;
for(int i=;i<=;i++)
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
fac[m]=;
for(int i=;i<=m;i++)fac[m]=1ll*fac[m]*i%mod;
for(int i=m+;i<=;i++)
fac[i]=1ll*fac[i-]*i%mod*inv[i-m]%mod;
Inv=qp(fac[m],mod-);
ans=;
for(int i=,j;i<=n;i=j+){
j=n/(n/i);
UPD(ans,1ll*(gets(j)-gets(i-)+mod)%mod*C(n/i+m-)%mod);
}
printf("%d\n",ans);
}
return ;
}
T2,暴力找循环节,然后用bsgs的思想$O(\sqrt{p})$做就可以了。但是复杂度不够优秀,我们考虑打表找规律,f[i]表示%i意义下的循环节长度,可以发现,若$(x,y)=1$,$f[x*y]=lcm(f[x],f[y])$,若$x=p^{k}$ ,$f[x]=f[p] \cdot p^{k-1}$,然后若$p%10=1|p%10=9$,$f[i]=p-1$,然后这样需要做bsgs的数就很少了,就可以愉快的AC了。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define int unsigned long long
using namespace std;
int a,b,n,k,p,T,len[];
struct mart{
int a[][];
mart operator * (mart B){
mart C;
C.a[][]=(a[][]*B.a[][]%p+a[][]*B.a[][]%p)%p;
C.a[][]=(a[][]*B.a[][]%p+a[][]*B.a[][]%p)%p;
C.a[][]=(a[][]*B.a[][]%p+a[][]*B.a[][]%p)%p;
C.a[][]=(a[][]*B.a[][]%p+a[][]*B.a[][]%p)%p;
return C;
}
}A,B,d,e,f;
mart qp(mart a,int b){
mart c;
c.a[][]=c.a[][]=;
c.a[][]=c.a[][]=;
for(;b;b>>=,a=a*a)
if(b&)c=c*a;
return c;
}
int getg(int n){
if(!n)return a%p;
A.a[][]=;A.a[][]=;
A.a[][]=p-;A.a[][]=;
B.a[][]=b%p;B.a[][]=a%p;
B.a[][]=B.a[][]=;
B=B*qp(A,n-);
return B.a[][];
}
unordered_map<int,int> hh;
unordered_map<int,int> mm;
int getp(int n){
if(n%==||n%==)return n-;
if(mm.count(n))return mm[n];
hh.clear();p=n;
int x=ceil(sqrt(*n)),last=,now=,tmp;
d.a[][]=;d.a[][]=;d.a[][]=d.a[][]=;
e.a[][]=;e.a[][]=;e.a[][]=p-;e.a[][]=;
f.a[][]=f.a[][]=;f.a[][]=f.a[][]=;
for(int i=;i<x;i++){
if(hh.count(now*+last))return i;
hh[now*+last]=i;
tmp=now;now=(now*%p-last+p)%p;last=tmp;
f=f*e;
}
d=d*f*f;
for(int i=;i<=x;i++,d=d*f){
if(hh.count(d.a[][]*+d.a[][]))
return mm[n]=(i+)*x-hh[d.a[][]*+d.a[][]];
}
}
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
int lcm(int x,int y){return x/gcd(x,y)*y;}
int getl(int x){
if(mm.count(x))return mm[x];
int y=x,ans=;
for(int i=;i*i<=y;i++)if(y%i==){
int w=getp(i);y/=i;
while(y%i==)w=w*i,y/=i;
ans=lcm(ans,w);
}
if(y!=)ans=lcm(ans,getp(y));
return mm[x]=ans;
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld%lld%lld",&a,&b,&n,&k,&p);
len[]=p;
for(int i=;i<=k;i++)len[i]=getl(len[i-]);
for(int i=k;i;i--)p=len[i],n=getg(n);
printf("%lld\n",n);
}
return ;
}
T3,真$\cdot$神题。首先如果c=0,可以直接暴力。
我们考虑$b=0$的情况,我们现在要求
$$y_{p}=\sum_{i=0}^{n-1}{A[i]*x_{p}^{i}}$$
$$\sum_{i=0}^{n-1}{A[i] \sum_{j=0}^{i} C_{i}^{j} \cdot d^{j} \cdot e^{i-j} \cdot c^{2pj}}$$
$$\sum_{j=0}^{n-1}{ c^{(p+j)^{2}-p^{2}-j^{2}} \cdot d^{j} \cdot \frac{1}{j!} \sum _{i=j+k}{A[i] \cdot {i!} \cdot e^{k} \cdot \frac{1}{k!} } }$$
$$\frac{1}{c^{p^{2}}} \sum_{x=p+j}{ c^{x^{2}} \cdot d^{j} \cdot \frac{1}{c^{j^{2}}} \cdot \frac{1}{j!} \sum_{i=j+k}{A[i] \cdot {i!} \cdot e^{k} \cdot \frac{1}{k!} } }$$
然后我们就可以通过两次卷积来统计这个答案啦。
于是我们就愉快的解决了$b=0$的情况。
下面考虑通解,我们发现$bc^{4k}+dc^{2k}+e$这个式子很不美丽,于是我们考虑将其化成$b(c^{2k}+d’)^{2}+e’$,解方程解得$d'=\frac{d}{2b}$,$e'=e-\frac{d^{2}}{4b}$。
ps.下面的d即为d',e即为e'。
所以我们现在要求的就是
$$\sum_{i=0}^{n-1}{A[i] (b(c^{2p}+d)^{2}+e)^{i} }$$
$$\sum_{i=0}^{n-1}{A[i] \sum_{j=0}^{i} C_{i}^{j} \cdot b^{j} \cdot (c^{2p}+d)^{2j} \cdot e^{i-j} }$$
$$\sum_{j=0}^{n-1} { (c^{2p}+d)^{2j} \cdot b^{j} \cdot \frac{1}{j!} \sum_{i=j+k} {A[i] \cdot i! \cdot e^{k} \cdot \frac{1}{k!} } }$$
我们按照上面$b=0$的做法将后面的东西卷积,现在我们就是要求
$$\sum_{j=0}^{n-1} { (c^{2p}+d)^{2j} \cdot A[j] }$$
$$\sum_{j=0}^{n-1} { A[j] \sum_{k+l=2j}{ C_{2j}^{k} \cdot c^{(p+k)^{2}-p^{2}-k^{2}} \cdot d^{l} } }$$
$$\frac{1}{c^{p^{2}}} \sum_{x=p+k} { c^{x^{2}} \cdot \frac{1}{c^{k^{2}}} \cdot \frac{1}{k!} \sum_{k+l=2j} { d^{l} \cdot \frac{1}{l!} \cdot A[j] \cdot {(2j)!} } } $$
我们发现到这里就可以卷积啦!只是细节比较麻烦。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 555555
#define mod 1000003
#define LL long long
using namespace std; int a[N],b[N],c[N],d[N],e[N],f[N],g[N];
int n,B,C,D,E;
int pw_b[N],pw_d[N],pw_e[N],pw_c[N],inv_c[N],fac[N],inv[N]; LL mul(LL a,LL b,LL p){
return (a*b-(LL)(((long double)a*b+0.5)/(long double)p)*p+p)%p;
}
int qp(int a,int b,int p=mod){
int c=;
for(;b;b>>=,a=1ll*a*a%p)
if(b&)c=1ll*c*a%p;
return c;
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
} struct poly{
int p,g,w[N],rev[N];
void prepare(int l){
w[]=;int dan=qp(g,(p-)/l,p);
for(int i=;i<=l;i++)w[i]=1ll*w[i-]*dan%p;
for(int i=;i<=l;i++){
if(i&)rev[i]=(rev[i>>]>>)|(l>>);
else rev[i]=rev[i>>]>>;
}
}
void dft(int *a,int o,int l){
for(int i=;i<l;i++)
if(i<rev[i])swap(a[i],a[rev[i]]);
for(int k=,t;k<=l;k<<=){
for(int i=;i<l;i+=k){
for(int j=;j<(k>>);j++){
t=1ll*(o==?w[l/k*j]:w[l-l/k*j])*a[i+j+(k>>)]%p;
a[i+j+(k>>)]=(a[i+j]-t+p)%p;a[i+j]=(a[i+j]+t)%p;
}
}
}
int inv=qp(l,p-,p);
if(o==-)for(int i=;i<l;i++)a[i]=1ll*a[i]*inv%p;
}
void mul(int *a,int *b,int *c,int l){
static int tmpa[N],tmpb[N];
prepare(l);
for(int i=;i<l;i++)tmpa[i]=a[i],tmpb[i]=b[i];
dft(tmpa,,l);dft(tmpb,,l);
for(int i=;i<l;i++)c[i]=1ll*tmpa[i]*tmpb[i]%p;
dft(c,-,l);
}
void revmul(int *a,int *b,int *c,int l){
static int tmpb[N];
for(int i=;i<l;i++)tmpb[i]=b[i];
reverse(tmpb,tmpb+l);
mul(a,tmpb,c,l);
reverse(c,c+l);
}
}P[];
void revmul(int *a,int *b,int *c,int l,int l1){
static int tmp[][N];
int L;for(L=;L<=l;L<<=);
P[].revmul(a,b,tmp[],L);
P[].revmul(a,b,tmp[],L);
LL Mod=1ll*P[].p*P[].p,v;
for(int i=;i<l1;i++){
v=;
v=(v+mul(1ll*tmp[][i]*P[].p%Mod,1ll*qp(P[].p,P[].p-,P[].p)%Mod,Mod))%Mod;
v=(v+mul(1ll*tmp[][i]*P[].p%Mod,1ll*qp(P[].p,P[].p-,P[].p)%Mod,Mod))%Mod;
c[i]=v%mod;
}
}
namespace work1{
void Main(){
int x=(B+D+E)%mod,y=,now=;
for(int i=;i<n;i++,now=1ll*now*x%mod)
UPD(y,1ll*a[i]*now%mod);
printf("%d\n",y);
x=E;y=;now=;
for(int i=;i<n;i++,now=1ll*now*x%mod)
UPD(y,1ll*a[i]*now%mod);
for(int i=;i<n;i++)printf("%d\n",y);
}
}
namespace work2{
void Main(){
fac[]=;for(int i=;i<=*n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[*n]=qp(fac[*n],mod-);for(int i=*n;i;i--)inv[i-]=1ll*inv[i]*i%mod;
pw_d[]=;for(int i=;i<=*n;i++)pw_d[i]=1ll*pw_d[i-]*D%mod;
pw_e[]=;for(int i=;i<=*n;i++)pw_e[i]=1ll*pw_e[i-]*E%mod;
for(int i=;i<=*n;i++)pw_c[i]=qp(C,1ll*i*i%(mod-)),inv_c[i]=qp(pw_c[i],mod-);
for(int i=;i<n;i++)a[i]=1ll*a[i]*fac[i]%mod;
for(int i=;i<n;i++)b[i]=1ll*pw_e[i]*inv[i]%mod;
revmul(b,a,c,*n,n);
for(int i=;i<n;i++)c[i]=1ll*c[i]*inv_c[i]%mod*inv[i]%mod*pw_d[i]%mod;
for(int i=;i<*n;i++)d[i]=pw_c[i];
revmul(c,d,e,*n,n);
for(int i=;i<n;i++){
e[i]=1ll*e[i]*inv_c[i]%mod;
printf("%d\n",e[i]);
}
}
}
namespace work3{
void Main(){
D=1ll*D*qp(*B,mod-)%mod;
E=(E-1ll*B*D%mod*D%mod+mod)%mod;
fac[]=;for(int i=;i<=*n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[*n]=qp(fac[*n],mod-);for(int i=*n;i;i--)inv[i-]=1ll*inv[i]*i%mod;
pw_b[]=;for(int i=;i<=*n;i++)pw_b[i]=1ll*pw_b[i-]*B%mod;
pw_d[]=;for(int i=;i<=*n;i++)pw_d[i]=1ll*pw_d[i-]*D%mod;
pw_e[]=;for(int i=;i<=*n;i++)pw_e[i]=1ll*pw_e[i-]*E%mod;
for(int i=;i<=*n;i++)pw_c[i]=qp(C,1ll*i*i%(mod-)),inv_c[i]=qp(pw_c[i],mod-);
for(int i=;i<n;i++)a[i]=1ll*a[i]*fac[i]%mod;
for(int i=;i<n;i++)b[i]=1ll*pw_e[i]*inv[i]%mod;
revmul(b,a,c,*n,n);
for(int i=*n-;~i;i--){
if(i&)c[i]=;
else c[i]=1ll*c[i>>]*fac[i]%mod*inv[i>>]%mod*pw_b[i>>]%mod;
}
for(int i=;i<*n;i++)d[i]=1ll*pw_d[i]*inv[i]%mod;
revmul(d,c,e,*n,*n);
for(int i=;i<*n;i++)e[i]=1ll*e[i]*inv[i]%mod*inv_c[i]%mod;
for(int i=;i<*n;i++)f[i]=pw_c[i];
revmul(e,f,g,*n,n);
for(int i=;i<n;i++){
g[i]=1ll*g[i]*inv_c[i]%mod;
printf("%d\n",g[i]);
}
}
}
int main(){
P[].p=;P[].g=;
P[].p=;P[].g=;
scanf("%d%d%d%d%d",&n,&B,&C,&D,&E);
for(int i=;i<n;i++)scanf("%d",&a[i]);
if(!C){work1::Main();return ;}
if(!B){work2::Main();return ;}
work3::Main();return ;
}
4.23
开场看T1,第二个样例不太明白,然后果断弃疗去看T2,怎么又是这个题啊,三遍了。数据越来越强,只会10分暴力。看T3,这这这,矩阵树+多项式??两个变量?把另一个强行设为$x^{n}$,算了一下复杂度,$O(n^{5}+n^{6})$,貌似很不可过啊,但是感觉这个是最可写的了,然后码码码,过了大样例,造了组极限数据,90s。。之后卡了卡,卡到了50s,感觉很绝望,之后去看T1,玩出来了样例2,然后我就陷入了一个思维误区,觉得每次的决策是确定的,然后就在想怎么比较两个决策的优劣,然后乱搞了好久也没搞出来,最后打了个假的暴搜,打完就剩20min了,赶紧去把T2的10分暴力写了。20+10+50=80 rank4/10。
T1,我们发现问题其实就是要求出每一轮妹子的胜率,我们设p为胜率,q为负率,发现就是要最大化$\frac{p}{q}$,然后我们二分他,然后倒着dp,或者说贪心?每次按照当前最优策略转移,就可以了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define eps 1e-6
using namespace std;
int n,m1,m2;
double f[][],p[][];
bool check(double x){
for(int i=;i<=*n+;i++){
if(i<=n)f[n+][i]=-x;
if(i==n+)f[n+][i]=;
if(i>n+)f[n+][i]=;
}
double r,s,t,w,d,l;
for(int i=n;i;i--){
r=p[i][],s=p[i][],t=p[i][];
for(int j=;j<=*n+;j++){
w=f[i+][j+],d=f[i+][j],l=f[i+][j-];
f[i][j]=max(max(r*w+s*d+t*l,s*w+t*d+r*l),t*w+r*d+s*l);
}
}
return f[][n+]>;
}
int main(){
//freopen("rps0.in","r",stdin);
while(scanf("%d%d%d",&n,&m1,&m2)==&&((n+m1+m2)!=)){
for(int i=;i<=n;i++){
scanf("%lf%lf%lf",&p[i][],&p[i][],&p[i][]);
p[i][]/=100.0;p[i][]/=100.0;p[i][]/=100.0;
}
double l=,r=,mid;
while(l+eps<=r){
mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
mid=1.0-1.0/(1.0+mid);
memset(f,,sizeof f);
f[][m2+]=;
for(int i=;i<=;i++){
for(int j=;j<m1+m2+;j++){
f[i+][j-]+=f[i][j]*(-mid);
f[i+][j+]+=f[i][j]*mid;
}
f[i+][]+=f[i][];
f[i+][m1+m2+]+=f[i][m1+m2+];
}
printf("%0.5f\n",f[][m1+m2+]);
}
return ;
}
T2,咕咕咕
T3,咕咕咕,正解是二维拉格朗日插值,复杂度貌似是$O(n^{5}+n^{4})$,还没有看懂。
但是可以用高斯消元水过,因为常数比较优秀。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1666
#define mod 1000000007
using namespace std;
int a[][],n,m,n1,n2,b[][],c[],f[],cnt;
int du[],dv[],dw[],ans;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
int work(){
int ans=;
for(int k=;k<n;k++){
if(!a[k][k]){
for(int i=k+;i<n;i++)if(a[i][k]){
for(int j=k;j<n;j++)swap(a[k][j],a[i][j]);
ans=mod-ans;
break;
}
}
if(!a[k][k])return ;
ans=1ll*ans*a[k][k]%mod;
int inv=qp(a[k][k],mod-);
for(int i=k+;i<n;i++){
int t=1ll*a[i][k]*inv%mod;
for(int j=k;j<n;j++)
UPD(a[i][j],mod-1ll*t*a[k][j]%mod);
}
}
return ans;
}
void add(int u,int v,int w){
UPD(a[u][u],w);
UPD(a[v][v],w);
UPD(a[u][v],mod-w);
UPD(a[v][u],mod-w);
}
void gauss(){
for(int k=;k<=cnt;k++){
if(!b[k][k]){
for(int i=k+;i<=cnt;i++)if(b[i][k]){
for(int j=k;j<=cnt;j++)swap(b[k][j],b[i][j]);
swap(c[k],c[i]);
break;
}
}
int ny=qp(b[k][k],mod-);
for(int i=k+;i<=cnt;i++){
int t=1ll*b[i][k]*ny%mod;
for(int j=k;j<=cnt;j++)
UPD(b[i][j],mod-1ll*b[k][j]*t%mod);
UPD(c[i],mod-1ll*c[k]*t%mod);
}
}
for(int i=cnt;i;i--){
for(int j=i+;j<=cnt;j++)
UPD(c[i],mod-1ll*b[i][j]*f[j]%mod);
f[i]=1ll*c[i]*qp(b[i][i],mod-)%mod;
}
}
int g[][][];
int main(){
scanf("%d%d%d%d",&n,&m,&n1,&n2);
for(int i=;i<=m;i++){
scanf("%d%d%d",&du[i],&dv[i],&dw[i]);
g[du[i]][dv[i]][dw[i]]++;
}
for(int x=;x<n;x++){
for(int y=;y<n-x;y++){
memset(a,,sizeof a);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)
for(int k=;k<=;k++)if(g[i][j][k]){
if(k==)add(i,j,g[i][j][k]);
if(k==)add(i,j,x*g[i][j][k]);
if(k==)add(i,j,y*g[i][j][k]);
}
c[++cnt]=work();
for(int i=,now=,nx=;i<n;i++){
for(int j=,ny=;j<n-i;j++){
b[cnt][++now]=1ll*nx*ny%mod;
ny=1ll*ny*y%mod;
}
nx=1ll*nx*x%mod;
}
}
}
gauss();
for(int i=,now=;i<n;i++){
for(int j=;j<n-i;j++,now++)
if(i<=n1&&j<=n2)UPD(ans,f[now]);
}
printf("%d\n",ans);
return ;
}
4.24
先看T1,发现40分sb主席树乱搞,后面的离线可以莫队?然后看T2,看起来可以线筛+杜教筛,看T3,感觉就10分暴力可写。然后回去码T1,写完40分,和暴力拍上了,然后想w=1的,yy了半个多小时,没有想出来。然后去写T2,写了个O(40n)的筛(我是傻逼),又推了推式子,没推出来,然后拿着k=40的数据去猜k=1的结论,啥也没发现。然后还有1h左右,在T1的离线和T3的暴力中选择了T1,写了30min左右写完了,感觉很开心,发现还有时间去写T3,然后手贱测了组极限数据,7s。。。然后致力卡常办小时,还是没卡进去。40+30+0=70 rank6/10。这两天暴力分不是挂了就是没有拿满,以后要注意这个问题。
T1,我们考虑对于每一个查询,可以改变每个点作出的贡献,然后二分答案,计算小于ans且个数小于w的数的个数,于是我们对于一个权值,将他最后w个的权值附成1,倒数第w+1个的权值设成-w。然后我们可以知道对于一个数,他对哪些区间的贡献是1,-w。这可以对应到平面上的一个矩形,然后我们就相当于是矩形加,单点求和。这个可以主席数套线段树来做,外层是关于左端点的可持久化权值线段树,内层是关于右端点的线段树。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 100500
using namespace std;
vector <int> v[N];
int n,w,q,type,a[N],np[N],nnp[N];
int lazy[*N],ls[*N],rs[*N],tot;
int root[N],rt[*N],lon[*N],ron[*N],sz;
void _update(int &rt,int l,int r,int x,int y,int z){
if(!rt)rt=++tot;
if(x<=l&&r<=y){lazy[rt]+=z;return ;}
int mid=(l+r)>>;
if(x<=mid)_update(ls[rt],l,mid,x,y,z);
if(y>mid)_update(rs[rt],mid+,r,x,y,z);
}
void _merge(int o,int &rt,int l,int r){
if(!o)return ;
if(!rt){rt=o;return ;}
lazy[rt]+=lazy[o];
if(l==r)return ;
int mid=(l+r)>>;
_merge(ls[o],ls[rt],l,mid);
_merge(rs[o],rs[rt],mid+,r);
}
int _query(int o,int rt,int l,int r,int x){
if(l==r)return lazy[rt]-lazy[o];
int mid=(l+r)>>;
if(x<=mid)return _query(ls[o],ls[rt],l,mid,x)+lazy[rt]-lazy[o];
else return _query(rs[o],rs[rt],mid+,r,x)+lazy[rt]-lazy[o];
}
void update(int &rt,int l,int r,int x,int nl,int nr,int nx){
if(!rt)rt=++sz;
_update(::rt[rt],,n,nl,nr,nx);
if(l==r)return ;
int mid=(l+r)>>;
if(x<=mid)update(lon[rt],l,mid,x,nl,nr,nx);
else update(ron[rt],mid+,r,x,nl,nr,nx);
}
void merge(int o,int &rt,int l,int r){
if(!o)return ;
if(!rt){rt=o;return;}
_merge(::rt[o],::rt[rt],,n);
if(l==r)return ;
int mid=(l+r)>>;
merge(lon[o],lon[rt],l,mid);
merge(ron[o],ron[rt],mid+,r);
}
int query(int o,int rt,int l,int r,int x,int k){
if(l==r)return l;
int mid=(l+r)>>;
int now=_query(::rt[lon[o]],::rt[lon[rt]],,n,x);
if(now>=k)return query(lon[o],lon[rt],l,mid,x,k);
else return query(ron[o],ron[rt],mid+,r,x,k-now);
}
int main(){
scanf("%d%d%d%d",&n,&w,&q,&type);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<=n;i++){
v[a[i]].push_back(i);
if(v[a[i]].size()>w){
np[v[a[i]][v[a[i]].size()-w-]]=i;
if(v[a[i]].size()>w+)
nnp[v[a[i]][v[a[i]].size()-w-]]=i;
}
}
for(int i=;i<=n;i++){
if(!np[i])np[i]=n+;
if(!nnp[i])nnp[i]=n+;
update(root[i],,n-,a[i],i,np[i]-,);
if(np[i]<=n)update(root[i],,n-,a[i],np[i],nnp[i]-,-w);
merge(root[i-],root[i],,n-);
}
int ans=;
for(int t=,l,r,k,now;t<=q;t++){
scanf("%d%d%d",&l,&r,&k);
if(type)l^=ans,r^=ans,k^=ans;
now=_query(rt[root[l-]],rt[root[r]],,n,r);
if(now<k)ans=n;
else ans=query(root[l-],root[r],,n-,r,k);
printf("%d\n",ans);
}
return ;
}
T2,容斥来求$\sum_{i=1}^{n}{f_{d}{[n]}}$,我们设$\lambda{[i]}=f_{\infty}{[i]}$,我们现在要求的就是
$$F_{d}{[n]}=\sum_{i=1}^{n}{\mu{[i]} \sum_{j=1}^{ \lfloor \frac{n}{i^{d+1}} \rfloor} {\lambda{[i^{d+1} \cdot j ]}} }$$
然后因为$\lambda$是完全积性函数,所以我们把关于i的都提到外面,我们现在还需要求一个$\sum{\lambda}$,然后我们发现$\lambda \otimes 1 = [x \; is \; Perfect \;Square]$,然后就可以杜教筛了。
#include <bits/stdc++.h>
#define N 5005000
#define int long long
using namespace std;
int prime[N/],tot,n,m,ans;
bool vis[N];
int mu[N],lmd[N],slmd[N],phi[N],sphi[N],pw[][];
int f[N],sf[N],tim[N],mx[N];
unordered_map<int,int> hphi,hlmd;
void init(){
mu[]=;mx[]=;
lmd[]=slmd[]=;
phi[]=sphi[]=;
for(int i=;i<=;i++){
if(!vis[i]){
prime[++tot]=i;
phi[i]=i-;mu[i]=lmd[i]=-;
tim[i]=mx[i]=;
}
for(int j=;j<=tot&&i*prime[j]<=;j++){
vis[i*prime[j]]=;
lmd[i*prime[j]]=-lmd[i];
if(i%prime[j]==){
tim[i*prime[j]]=tim[i]+;
mx[i*prime[j]]=max(mx[i],tim[i]+);
phi[i*prime[j]]=phi[i]*prime[j];
mu[i*prime[j]]=;
break;
}
phi[i*prime[j]]=phi[i]*phi[prime[j]];
mu[i*prime[j]]=-mu[i];
tim[i*prime[j]]=;
mx[i*prime[j]]=mx[i];
}
sphi[i]=sphi[i-]+phi[i];
slmd[i]=slmd[i-]+lmd[i];
}
for(int i=;i<=;i++){
f[i]=lmd[i]*max(0ll,m-mx[i]+);
sf[i]=sf[i-]+f[i];
}
for(int i=;i<=;i++){
pw[i][]=;
for(int j=;j<=;j++){
pw[i][j]=pw[i][j-]*i;
if(pw[i][j]>10000000000ll)break;
}
}
}
int getsphi(int n){
if(n<=)return sphi[n];
if(hphi.count(n))return hphi[n];
int ans=1ll*n*(n+)/;
for(int i=,j;i<=n;i=j+){
j=n/(n/i);
ans-=(j-i+)*getsphi(n/i);
}
return hphi[n]=ans;
}
int getslmd(int n){
if(n<=)return slmd[n];
if(hlmd.count(n))return hlmd[n];
int ans=sqrt(n);
for(int i=,j;i<=n;i=j+){
j=n/(n/i);
ans-=(j-i+)*getslmd(n/i);
}
return hlmd[n]=ans;
}
int getf(int d,int x){
int ans=;
for(int i=;pw[i][d+]&&pw[i][d+]<=x;i++)
ans+=((d&)?():(lmd[i]))*mu[i]*getslmd(x/pw[i][d+]);
return ans;
}
int getsf(int x){
if(x<=)return sf[x];
int ans=;
for(int d=;d<=m;d++)ans+=getf(d,x);
return ans;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
signed main(){
scanf("%lld%lld",&n,&m);
init();
for(int i=,j,last=,now;i<=n;i=j+){
j=n/(n/i);
now=getsf(j);
ans+=(now-last)*(*getsphi(n/i)-);
last=now;
}
printf("%lld\n",ans&);
}
T3,我们利用期望的线性性,考虑求出每个点的贡献,即每个点的期望经过次数*其对应的距离,我们考虑如何求出一个状态下某个点的期望经过次数,因为选点是随机的,所以我们只需统记若干1的状态下0,1的点的期望经过次数,我们考虑在这一层算下一步造成的贡献,于是转移方程就有了。
$$f_{i,0}=\frac{i}{n} f_{i-1,0} + \frac{n-i-1}{n} f_{i+1,0} +\frac{1}{n}(f_{i+1,1}+[notend_{i+1}])$$
$$f_{i,1}=\frac{i-1}{n}f_{i-1,1} +\frac{n-i}{n} f_{i+1,1} +\frac{1}{n}(f_{i-1,0}+[notend_{i-1}])$$
然后我们知道$f_{0/n,0/1}=0$,我们在设$f_{1,0}=x,f_{1,1}=y$,然后推到$f_{n-1}$,然后我们就可以解二元一次方程组了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 100500
using namespace std;
int n,num1,inv[N],ans;
char s[N];
struct data{
int x,y,z;
data(){x=y=z=;}
data(int a,int b,int c){x=a;y=b;z=c;}
data operator + (data a){return data((x+a.x)%mod,(y+a.y)%mod,(z+a.z)%mod);}
data operator - (data a){return data((x-a.x+mod)%mod,(y-a.y+mod)%mod,(z-a.z+mod)%mod);}
data operator * (int a){return data(1ll*x*a%mod,1ll*y*a%mod,1ll*z*a%mod);}
}f[N][],one;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
namespace graph{
int e=,head[N],d1[N],d2[N],d[N],size[N];
struct edge{
int v,next;
}ed[N<<];
void add(int u,int v){
ed[e].v=v;ed[e].next=head[u];
head[u]=e++;
}
void dfs1(int x,int f){
size[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==f)continue;
dfs1(v,x);
d1[x]+=d1[v]+size[v];
size[x]+=size[v];
}
}
void dfs2(int x,int f){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==f)continue;
d2[v]=d2[x]+(d1[x]-d1[v]-size[v])+(n-size[v]);
dfs2(v,x);
}
d[x]=1ll*(d1[x]+d2[x])*inv[n]%mod;
}
}
using namespace graph;
int main(){
//freopen("test.in","r",stdin);
scanf("%d",&n);
scanf("%s",s+);
for(int i=;i<=n;i++)
if(s[i]=='')num1++;
inv[]=inv[]=;
for(int i=,x;i<=n;i++){
scanf("%d",&x);
add(x,i);add(i,x);
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
}
dfs1(,);dfs2(,); f[][]=data(,,);
f[][]=data(,,);
f[][]=data(,,);
f[][]=data(,,);
one=data(,,); f[][]=(f[][]*n)*inv[n-];
f[][]=(f[][]*n-f[][]-one)*inv[n-];
for(int i=;i<n;i++){
f[i][]=(f[i-][]*n-f[i-][]*(i-)-f[i-][]-one)*inv[n-i+];
f[i][]=(f[i-][]*n-f[i-][]*(i-)-f[i][]-one)*inv[n-i];
}
f[n-][]=f[n-][]*n-f[n-][]*(n-)-f[n-][]-(n==?data(,,):one);
f[n-][]=f[n-][]*n-f[n-][]*(n-); //printf("%d %d %d\n",f[n-1][1].x,f[n-1][1].y,f[n-1][1].z);
//printf("%d %d %d\n",f[n-1][0].x,f[n-1][0].y,f[n-1][0].z); int a1=f[n-][].x,b1=f[n-][].y,c1=mod-f[n-][].z;
int a2=f[n-][].x,b2=f[n-][].y,c2=mod-f[n-][].z;
int d1=(1ll*c1*a2%mod-1ll*c2*a1%mod+mod)%mod;
int d2=(1ll*b1*a2%mod-1ll*b2*a1%mod+mod)%mod;
int y=1ll*d1*qp(d2,mod-)%mod;
int x=1ll*(c1-1ll*b1*y%mod+mod)%mod*qp(a1,mod-)%mod; //printf("x==%d y==%d\n",x,y); int f0=((1ll*f[num1][].x*x%mod+1ll*f[num1][].y*y%mod)%mod+f[num1][].z)%mod;
int f1=((1ll*f[num1][].x*x%mod+1ll*f[num1][].y*y%mod)%mod+f[num1][].z)%mod; //printf("f0==%d f1==%d\n",f0,f1); for(int i=;i<=n;i++){
if(s[i]=='')UPD(ans,1ll*(f0+inv[n])*d[i]%mod);
else UPD(ans,1ll*(f1+inv[n])*d[i]%mod);
}
printf("%d\n",ans);
return ;
}
4.26
今天这套题,充分暴露了我的问题,不能怪题,还是自己弱。
开场先读题,发现T1计算几何好像很清真,T2貌似是回文自动机?T3是DP?然后思考了20minT1,开始写,写了1h左右写完了,然后造了几组小样例调了调,然后过不了大样例,然后开始死磕,然后还有90min时发现后两题还没看,然后把暴力都写上了,接着调,最后一个小时手玩发现样例好像是错的,然后就崩溃了。最后看了一眼T3,发现好像是傻逼网络流,没时间打了。80+30+20=130 rank7,wq,zzhAK了??T1可以忽略多边形???这么傻逼,T3果然是sb网络流,T2貌似也是sb题,然后就GG了。后来发现是我重心求错了,他要求整个面积的重心,而我求的是多边形的重心。
T1,数据太水了,我的代码好像也不对。就是大模拟。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
#define eps 1e-8
const double pi=acos(-1.0);
struct point{
double x,y;
point (){x=y=;}
point (double a,double b){x=a;y=b;}
point operator + (point a){return point(x+a.x,y+a.y);}
point operator - (point a){return point(x-a.x,y-a.y);}
double operator * (point a){return x*a.y-y*a.x;}
point operator * (double a){return point(x*a,y*a);}
point operator / (double a){return point(x/a,y/a);}
}sun,ear,g,p[],np;
int n;
double t1,t2,t,R,alp,dis,alpl,alpr,pl,pr,ans;
double getdis(point a){
return sqrt(a.x*a.x+a.y*a.y);
}
bool cross1(point a){
for(int i=;i<=n;i++)
if(getdis(p[i]-sun)<=getdis(a-sun)&&(sun-p[i])*(a-p[i])>=)return ;
return ;
}
bool cross2(point a){
for(int i=;i<=n;i++)
if(getdis(p[i]-sun)<=getdis(a-sun)&&(a-p[i])*(sun-p[i])>=)return ;
return ;
}
int main(){
scanf("%lf%lf%lf%lf%lf",&sun.x,&sun.y,&ear.x,&ear.y,&R);
scanf("%d%lf%lf%lf",&n,&t1,&t2,&t);
for(int i=;i<=n;i++)
scanf("%lf%lf",&p[i].x,&p[i].y);
for(int i=;i<=n;i++){
alp=atan2(p[i].y-ear.y,p[i].x-ear.x);
dis=getdis(p[i]-ear);
alp=alp+*pi/t1*t;
p[i]=point(ear.x+dis*cos(alp),ear.y+dis*sin(alp));
}
double S=;
for(int i=;i<=n;i++)
{
double tmp=p[i]*p[i==n?:i+];
g=g+((p[i]+p[i==n?:i+])*tmp);
S+=tmp;
}
g=g/(*S);
for(int i=;i<=n;i++){
alp=atan2(p[i].y-g.y,p[i].x-g.x);
dis=getdis(p[i]-g);
alp=alp+*pi/t2*t-*pi/t1*t;
p[i]=point(g.x+dis*cos(alp),g.y+dis*sin(alp));
}
dis=getdis(ear-sun);
dis=sqrt(dis*dis-R*R);
alp=atan2(sun.y-ear.y,sun.x-ear.x);
double l=,r=pi,mid;
while(l+eps<=r){
mid=(l+r)/2.0;
if(getdis(sun-point(ear.x+cos(alp+mid)*R,ear.y+sin(alp+mid)*R))<=dis)l=mid;
else r=mid;
}
alpl=alp-mid;alpr=alp+mid;
if(alpr<alpl)alpr+=*pi;
l=alpl,r=alpr;
while(l+eps<=r){
mid=(l+r)/2.0;
np=point(ear.x+cos(mid)*R,ear.y+sin(mid)*R);
if(cross1(np))r=mid;
else l=mid;
}
pl=mid;
l=pl,r=alpr;
while(l+eps<=r){
mid=(l+r)/2.0;
np=point(ear.x+cos(mid)*R,ear.y+sin(mid)*R);
if(cross2(np))l=mid;
else r=mid;
}
pr=mid;
ans=((alpr-pr)+(pl-alpl))*R;
printf("%0.2lf\n",ans);
return ;
}
T2,manacher水题。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 2000500
using namespace std;
int T,n,l[N],f[N],g[N],a1[N],b1[N],a2[N],b2[N],tot,ans,now,de;
char s[N],s1[N];
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
void manacher(){
int mx=,pos=;
for(int i=;i<=tot;i++){
if(i<mx)l[i]=min(l[*pos-i],mx-i);
else l[i]=;
while(i-l[i]>&&i+l[i]<=tot&&s1[i-l[i]]==s1[i+l[i]])l[i]++;
if(i+l[i]>mx)mx=i+l[i],pos=i;
if(i&){
int posl=(i-l[i]+)>>,posr=(i+l[i]-)>>,posn=(i+)>>;
if(posl>posr)continue;
UPD(a1[posl],posr);UPD(b1[posl],mod-);
UPD(a1[posn],mod-posn+);UPD(b1[posn],); UPD(a2[posr],posl);UPD(b2[posr],);
UPD(a2[posn-],mod-posn);UPD(b2[posn-],mod-);
}
else{
int posl=(i-l[i]+)>>,posr=(i+l[i]-)>>,posn=i>>;
if(posl>posr)continue;
UPD(a1[posl],posr);UPD(b1[posl],mod-);
UPD(a1[posn+],mod-posn+);UPD(b1[posn+],); UPD(a2[posr],posl);UPD(b2[posr],);
UPD(a2[posn-],mod-posn-);UPD(b2[posn-],mod-);
}
}
}
int main(){
//freopen("test.in","r",stdin);
scanf("%d",&T);
while(T--){
scanf("%s",s+);
n=strlen(s+);
ans=tot=;
memset(a1,,sizeof a1);
memset(b1,,sizeof b1);
memset(a2,,sizeof a2);
memset(b2,,sizeof b2);
s1[++tot]='#';
for(int i=;i<=n;i++){
s1[++tot]=s[i];
s1[++tot]='#';
}
manacher();
now=de=;
for(int i=;i<=n;i++){
UPD(now,de);
UPD(now,a1[i]);
UPD(de,b1[i]);
f[i]=now;
}
now=de=;
for(int i=n;i;i--){
UPD(now,de);
UPD(now,a2[i]);
UPD(de,b2[i]);
g[i]=now;
}
ans=;
for(int i=;i<n;i++)
UPD(ans,1ll*g[i]*f[i+]%mod);
printf("%d\n",ans);
}
return ;
}
T3,网络流水题,最大权闭合子图加上文理分科的思想。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf 0x3fffffff
#define N 150
using namespace std;
int Tim,n,ans,buc[],a[],b[],c[N],w[N][N];
char s[N];
int e=,head[N];
struct edge{
int u,v,f,next;
}ed[N*N<<];
void add(int u,int v,int f1,int f2){
ed[e].u=u;ed[e].v=v;ed[e].f=f1;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=f2;
ed[e].next=head[v];head[v]=e++;
}
int dep[N],S,T;
bool bfs(){
memset(dep,,sizeof dep);
dep[S]=;
queue<int> q;q.push(S);
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&!dep[v]){
dep[v]=dep[x]+;
if(v==T)return ;
q.push(v);
}
}
}
return ;
}
int dfs(int x,int f){
if(x==T||!f)return f;
int ans=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].v&&dep[v]==dep[x]+){
int nxt=dfs(v,min(f,ed[i].f));
ans+=nxt;f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
if(!ans)dep[x]=-;
return ans;
}
int dinic(){
int ans=;
while(bfs())ans+=dfs(S,inf);
return ans;
}
int sum[N];
int main(){
scanf("%d",&Tim);
while(Tim--){
scanf("%d",&n);ans=;
scanf("%s",s+);
for(int i=;i<=n;i++)c[i]=s[i]-'';
for(int i=;i<=;i++)
scanf("%d%d",&a[i],&b[i]);
memset(sum,,sizeof sum);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
scanf("%d",&w[i][j]);
if(i==j)continue;
sum[i]+=w[i][j];
sum[j]+=w[i][j];
ans+=*w[i][j];
}
e=;memset(head,,sizeof head);
S=n+;T=S+;
for(int i=;i<=;i++)add(n+i+,T,*(b[i]-a[i]),);
for(int i=;i<=n;i++){
add(S,i,sum[i],);
add(i,T,*a[c[i]],);
add(i,n+c[i]+,inf,);
for(int j=i+;j<=n;j++)
add(i,j,w[i][j]+w[j][i],w[i][j]+w[j][i]);
}
printf("%d\n",(ans-dinic())/);
}
return ;
}
RP上红,233。
加油吧。
4.27
吸取昨天的教训,先读了一遍题,发现T2原题,T1,T3都不会。然后T2换题了,看了看好像是裸插头。觉得T1是多项式,然后推,推了一个多小时,没推出来,puts了样例,然后去把T3的暴力打了,之后去看T2,写插头DP,写完发现看错数据范围了。然后过了样例,没怎么检查,然后去推T1的70分,感觉是个dp,还是没推出来。然后GG,5+11+30=46 rank6,T2,插头dp情况没讨论全挂了24分,T1貌似O(n^4)暴力dp很清真。然而正解是二分图生成树。T3分块。。。
T1,$O(n^{4})$,dp是枚举一个点所在树的大小,然后套路组合数转移。然后我们发现把奇数层和偶数层看成两种点,其实是求完全二分图的生成树个数,这个我们可以打表发现答案是$S(n,m)=n^{m-1}+m^{n-1}$,最后$ans=C_{n-1}^{m-1} \cdot S(n-m,m)$
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int n,m,mod,inv[];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
m=min(m,n-m);
int ans=;
for(int i=;i<=m;i++)
ans=1ll*ans*(n-i+)%mod;
inv[]=;
for(int i=;i<=m;i++){
inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
ans=1ll*ans*inv[i]%mod;
}
return ans;
}
int main(){
scanf("%d%d%d",&n,&m,&mod);
printf("%lld\n",1ll*C(n-,m-)*qp(m,n-m-)%mod*qp(n-m,m-)%mod);
return ;
}
T2,35分插头dp
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 35
#define inf 0x3fffffff
using namespace std;
int n,m,num,vis[N][N],c[N][N],d[N][N];
int now,last,mx,my,ans,Tim;
struct hash_table{
static const int P=;
int head[P+],nxt[P+],val[P+],tot,key[P+];
void clear(){
memset(head,,sizeof head);
tot=;
}
void add(int x,int v){
int y=x%P;
nxt[++tot]=head[y];head[y]=tot;
key[tot]=x;val[tot]=v;
}
int & operator [] (int x){
int y=x%P;
for(int i=head[y];i;i=nxt[i])
if(key[i]==x)return val[i];
add(x,-inf);
return val[tot];
}
}f[];
int gets(int x,int y){
return ((x>>((y-)*))&);
}
int find(int x,int y){
for(int i=y,cnt=,now,pos=(gets(x,y)==?:-);;i+=pos){
now=gets(x,i);
if(now==)cnt++;
if(now==)cnt--;
if(!cnt)return i;
}
}
void change(int &x,int y,int z){
x^=(gets(x,y)<<(*(y-)))^(z<<(*(y-)));
}
void dp(int i,int j){
swap(now,last);
f[now].clear();
for(int k=;k<=f[last].tot;k++){
int nf=f[last].val[k],s=f[last].key[k];
int x=gets(s,j),y=gets(s,j+);
if(x==&&y==){
change(s,j,);change(s,j+,);
if((!s)&&(i>mx||(i==mx&&j>=my)))
ans=max(ans,nf);
f[now][s]=max(f[now][s],nf);
}
else if(x==&&y==){
change(s,j,);change(s,j+,);
f[now][s]=max(f[now][s],nf);
}
else if(x==&&y==){
change(s,find(s,j+),);
change(s,j,);change(s,j+,);
f[now][s]=max(f[now][s],nf);
}
else if(x==&&y==){
change(s,find(s,j),);
change(s,j,);change(s,j+,);
f[now][s]=max(f[now][s],nf);
}
else if(!x&&!y){
if(!vis[i][j])f[now][s]=max(f[now][s],nf);
if(i==n||j==m)continue;
change(s,j,);change(s,j+,);
f[now][s]=max(f[now][s],nf+c[i][j]+d[i][j]);
}
else if(x&&!y){
if(i==n)continue;
f[now][s]=max(f[now][s],nf+d[i][j]);
}
else if(!x&&y){
if(j==m)continue;
f[now][s]=max(f[now][s],nf+c[i][j]);
}
}
}
int main(){
//freopen("bounce5d.in","r",stdin);
//freopen("1.out","w",stdout);
scanf("%d",&Tim);
while(Tim--){
scanf("%d%d",&n,&m);
//if(n>10&&m>10){puts("0");continue;}
if(n>=m){
for(int i=;i<=n;i++)
for(int j=;j<m;j++)
scanf("%d",&c[i][j]);
for(int i=;i<n;i++)
for(int j=;j<=m;j++)
scanf("%d",&d[i][j]);
memset(vis,,sizeof vis);
scanf("%d",&num);
mx=my=;
for(int i=,x,y;i<=num;i++){
scanf("%d%d",&x,&y);
vis[x][y]=;
if(x>mx)mx=x,my=y;
else if(x==mx&&y>my)my=y;
}
}
else{
swap(n,m);
for(int j=;j<=m;j++)
for(int i=;i<n;i++)
scanf("%d",&d[i][j]);
for(int j=;j<m;j++)
for(int i=;i<=n;i++)
scanf("%d",&c[i][j]);
memset(vis,,sizeof vis);
scanf("%d",&num);
mx=my=;
for(int i=,x,y;i<=num;i++){
scanf("%d%d",&y,&x);
vis[x][y]=;
if(x>mx)mx=x,my=y;
else if(x==mx&&y>my)my=y;
}
}
now=,last=,ans=-inf;
if(!num)ans=;
f[now].clear();
f[now][]=;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++)
dp(i,j);
swap(now,last);
f[now].clear();
for(int k=;k<=f[last].tot;k++)
f[now][f[last].key[k]<<]=max(f[now][f[last].key[k]<<],f[last].val[k]);
}
if(ans==-inf)puts("Impossible");
else printf("%d\n",ans);
}
}
正解网络流,其实还是套路,我们把每个点拆点,然后自己流一条S->x1->x2->T,然后黑白染色,分别是横进竖出,竖进横出,如果x可以到y,那么x1->y2连一条边,这样x2也需要一个流量,y1也需要出一个流量,就可以跑最小费用最大流了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 33
#define inf 0x3fffffff
using namespace std;
int n,m,num,vis[N][N],c[N][N],d[N][N],Tim;
int e,head[N*N<<];
struct edge{
int u,v,w,f,next;
}ed[(N*N)<<];
void add(int u,int v,int f,int w){
ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;ed[e].w=-w;
ed[e].next=head[v];head[v]=e++;
}
#define id(i,j) (((i)-1)*2+(j))
int dis[N*N<<],Flow,Cost,S,T,id[N][N],tot1,tot2;
int bo[N*N<<],tim;
int q[N*N*N],he,ta;
bool spfa(){
memset(dis,-0x3f,sizeof dis);
tim++;dis[S]=;
he=ta=;q[]=S;
while(he<=ta){
int x=q[he++];bo[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]<dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;
if(bo[v]!=tim){bo[v]=tim;q[++ta]=v;}
}
}
}
return dis[T]!=dis[];
}
int dfs(int x,int f){
bo[x]=tim;
if(x==T){
Cost+=dis[T]*f;
Flow+=f;
return f;
}
if(!f)return ;
int ans=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]==dis[x]+ed[i].w&&bo[v]!=tim){
int nxt=dfs(v,min(f,ed[i].f));
ans+=nxt;f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
return ans;
}
void work(){
Cost=Flow=;
while(spfa()){
do{
tim++;
dfs(S,inf);
}while(bo[T]==tim);
}
if(Flow!=n*m)puts("Impossible");
else printf("%d\n",Cost);
return ;
}
int main(){
//freopen("bounce6e.in","r",stdin);
//freopen("4.out","w",stdout);
scanf("%d",&Tim);
while(Tim--){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
for(int j=;j<m;j++)
scanf("%d",&c[i][j]);
for(int i=;i<n;i++)
for(int j=;j<=m;j++)
scanf("%d",&d[i][j]);
scanf("%d",&num);
memset(vis,,sizeof vis);
for(int i=,x,y;i<=num;i++){
scanf("%d%d",&x,&y);
vis[x][y]=;
}
tot1=tot2=;
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
if((i+j)&)id[i][j]=++tot1;
else id[i][j]=++tot2;
}
}
for(int i=;i<=n;i++)
for(int j=;j<=m;j++)
if(!((i+j)&))id[i][j]+=tot1;
S=n*m*+;T=S+;
e=,memset(head,,sizeof head);
for(int i=;i<=n;i++){
for(int j=;j<m;j++){
if((i+j)&)add(id(id[i][j],),id(id[i][j+],),,c[i][j]);
else add(id(id[i][j+],),id(id[i][j],),,c[i][j]);
}
}
for(int i=;i<n;i++){
for(int j=;j<=m;j++){
if((i+j)&)add(id(id[i+][j],),id(id[i][j],),,d[i][j]);
else add(id(id[i][j],),id(id[i+][j],),,d[i][j]);
}
}
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
add(S,id(id[i][j],),,);
add(id(id[i][j],),T,,);
if(!vis[i][j])add(id(id[i][j],),id(id[i][j],),,);
}
}
work();
}
}
T3,分块,我们发现权值范围很小,然后分块时要保证每个块的权值范围不超过$len*sqrt{n}$,然后每$\sqrt{m}$次操作暴力重构。
感觉根号的思路都很巧妙,放到数列上就是分块莫队之类的,脑洞很大,思路清奇。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100500
using namespace std;
int e=,head[N];
struct edge{
int u,v,w,next;
}ed[N];
void add(int u,int v,int w){
ed[e].u=u;ed[e].v=v;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
}
int a[N],tot,L[N],R[N],cnt,be[N],fro[N],en[N];
int minn[],maxn[],buc[][],Low,High,mx,mn;
int n,m,len,nn,mm,lazy[];
void dfs(int x,int dep){
a[++tot]=dep;
L[x]=tot;
for(int i=head[x];i;i=ed[i].next)
dfs(ed[i].v,dep+ed[i].w);
R[x]=tot;
}
void build(){
if(cnt){
for(int i=;i<=cnt;i++){
for(int j=;j<=maxn[i]-minn[i];j++)
buc[i][j]=;
for(int j=fro[i];j<=en[i];j++)
a[j]+=lazy[i];
lazy[i]=;
}
cnt=;
}
mx=mn=a[];be[]=++cnt;fro[cnt]=;
for(int i=;i<=n;i++){
if(i-fro[cnt]>nn||max(mx,a[i])-min(mn,a[i])>nn*len){
maxn[cnt]=mx;minn[cnt]=mn;
en[cnt]=i-;fro[++cnt]=i;
be[i]=cnt;mx=mn=a[i];
}
else{
be[i]=cnt;
mx=max(mx,a[i]);
mn=min(mn,a[i]);
}
}
en[cnt]=n;maxn[cnt]=mx;minn[cnt]=mn;
Low=;High=;
//cerr<<cnt<<endl;
for(int i=;i<=cnt;i++){
for(int j=fro[i];j<=en[i];j++)
buc[i][a[j]-minn[i]]++;
for(int j=;j<=maxn[i]-minn[i];j++)
buc[i][j]+=buc[i][j-];
Low=min(Low,minn[i]);
High=max(High,maxn[i]);
}
}
void change(int x){
for(int i=;i<=maxn[x]-minn[x];i++)buc[x][i]=;
minn[x]=;maxn[x]=;
for(int i=fro[x];i<=en[x];i++){
a[i]+=lazy[x];
maxn[x]=max(maxn[x],a[i]);
minn[x]=min(minn[x],a[i]);
}
lazy[x]=;
for(int i=fro[x];i<=en[x];i++)
buc[x][a[i]-minn[x]]++;
for(int i=;i<=maxn[x]-minn[x];i++)
buc[x][i]+=buc[x][i-];
}
bool check(int l,int r,int x,int y){
int ans=;
if(be[l]==be[r]){
for(int i=l;i<=r;i++)if(a[i]+lazy[be[l]]<=x)ans++;
}
else{
for(int i=l;i<=en[be[l]];i++)if(a[i]+lazy[be[l]]<=x)ans++;
for(int i=fro[be[r]];i<=r;i++)if(a[i]+lazy[be[r]]<=x)ans++;
for(int i=be[l]+;i<be[r];i++){
if(minn[i]+lazy[i]<=x&&maxn[i]+lazy[i]>=x)
ans+=buc[i][x-lazy[i]-minn[i]];
else if(maxn[i]+lazy[i]<x)ans+=buc[i][maxn[i]-minn[i]];
}
}
return ans>=y;
}
int query(int l,int r,int y){
int low=Low,high=High,mid,fin=low-;
while(low<=high){
mid=(low+high)>>;
if(check(l,r,mid,y))fin=mid,high=mid-;
else low=mid+;
}
return fin;
}
int main(){
scanf("%d%d%d",&n,&m,&len);
nn=;mm=;
for(int i=,x,y;i<=n;i++){
scanf("%d%d",&x,&y);
add(x,i,y);
}
dfs(,);
build();
int o,x,y,l,r,tim=;
while(m--){
scanf("%d%d%d",&o,&x,&y);
if(o==){
l=L[x];r=R[x];
if(r-l+<y){puts("-1");continue;}
printf("%d\n",query(l,r,y));
}
else{
l=L[x];r=R[x];
if(be[l]==be[r]){
for(int i=l;i<=r;i++)a[i]+=y;
change(be[l]);
}
else{
for(int i=be[l]+;i<be[r];i++)lazy[i]+=y;
for(int i=l;i<=en[be[l]];i++)a[i]+=y;
for(int i=fro[be[r]];i<=r;i++)a[i]+=y;
change(be[l]);change(be[r]);
}
High+=y;
tim++;
}
if(tim==mm){
tim=;
build();
}
}
return ;
}
翻翻翻!
4.28
先看T1,感觉就很水,先写了个O(n^2)dp,然后观察推理了一波,发现了个规律,然后打上了。看T2,好像做过类似的,推了一会式子,没推出来,先写了30分暴力,然后发现50分好像也可以做,然后改了改,之后去看T3,一眼只会20分暴力,推了一下,发现a,b串字符集不相交时就是裸的矩阵乘,然后码码码,把两个部分分都写上了,懒得拍了,最后发现T2貌似可以乱dp一波,但是没时间了,肉眼查了波错就交了。100+30+60=190 rank3。T2挂分了。炸内存了,md,还没开long long,这还有分,老天有眼啊。裸暴力210,但是最高分205,落实暴力不挂分看来的确是很重要的。
T1,水题。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <map>
#define inf 0x3fffffff
#define int long long
using namespace std;
int pw[];
map<int,int> mm;
int getf(int x){
if(mm.count(x+))return ;
int ans=;
int pos=upper_bound(pw,pw+,x)-pw-;
for(int i=pos;~i;i--){
if(pw[i]<=x){
ans++;
x-=pw[i];
}
if(mm.count(x+))return ans;
}
return ans;
}
int n,m,ans,p[],vis1,visn;
signed main(){
//freopen("2.out","w",stdout);
for(int i=,now=;i<=;i++,now=2ll*now){
pw[i]=now;
mm[now]=;
}
scanf("%lld%lld",&n,&m);
for(int i=;i<=m;i++){
scanf("%lld",&p[i]);
if(p[i]==)vis1=;
if(p[i]==n)visn=;
}
if(!vis1)p[++m]=,ans++;
if(!visn&&n!=)p[++m]=n,ans++;
sort(p+,p+m+);
for(int i=;i<=m;i++)
ans+=getf(p[i]-p[i-]-);
printf("%lld\n",ans);
return ;
}
T2,我们考虑优化暴力dp,我们发现函数值是一个下凸的函数,而且总拐点是O(n)级别的,我们考虑在拐点处维护斜率的增量,这个可以用一个可并堆来实现。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 200050
#define LL long long
using namespace std;
int n,T;
int e,head[N];
struct edge{
int u,v,w,next;
}ed[N<<];
void add(int u,int v,int w){
ed[e].u=u;ed[e].v=v;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
}
LL f[N];
struct diui{
diui *ch[];
int x,val;
diui(int a,int b){
x=a;val=b;
ch[]=ch[]=NULL;
}
}*root[N];
diui *merge(diui *a,diui *b){
if(!a)return b;
if(!b)return a;
if((a->x>b->x)||(a->x==b->x&&a->val>b->val))swap(a,b);
a->ch[]=merge(a->ch[],b);
swap(a->ch[],a->ch[]);
return a;
}
void dfs(int x,int fa,int w){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x,ed[i].w);
}
root[x]=merge(new diui(,-),new diui(w,));
f[x]=w-;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
root[x]=merge(root[x],root[v]);
f[x]+=f[v];
}
int p=,v=,np,nv;
while(){
np=root[x]->x;nv=root[x]->val;
f[x]+=1ll*(np-p)*v;p=np;v+=nv;
root[x]=merge(root[x]->ch[],root[x]->ch[]);
if(v>=){root[x]=merge(root[x],new diui(np,v));break;}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
e=;memset(head,,sizeof head);
for(int i=,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
dfs(,,);
printf("%lld\n",f[]);
}
return ;
}
T3,很好的sam矩乘的题,我们发现字符集有交时可能一个串会被统计多次,于是我们考虑用一种定义方法使得其唯一对应一种状态,于是我们设$f_{i,sa,sb}$表示长度为i的串,被分成aba...aba这种的最后一个a的子串最短的分割方法其对应在a串的sam中的结点,sb同理,然后转移也很巧妙,不过这里地方太小,写不下。而且这样的状态数最多是8n左右,然后矩乘就可以。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define N 25
#define mod 1000000007
#define pr pair<int,int>
#define mk make_pair
#define fi first
#define se second
using namespace std;
int n,m,len,mm[N<<][N<<],tot,T,ml;
pr p[];queue<pr> q;
char sa[N],sb[N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
struct sam{
int last,tot,mx[N<<],ch[N<<][],par[N<<];
void add(int c){
int p=last,np=++tot;
mx[np]=mx[p]+;
for(;!ch[p][c];p=par[p])ch[p][c]=np;
if(!p)par[np]=;
else{
int q=ch[p][c];
if(mx[q]==mx[p]+)par[np]=q;
else{
int nq=++tot;
mx[nq]=mx[p]+;par[nq]=par[q];
memcpy(ch[nq],ch[q],sizeof ch[nq]);
par[q]=par[np]=nq;
for(;p&&ch[p][c]==q;p=par[p])ch[p][c]=nq;
}
}
last=np;
}
}sama,samb;
struct mart{
int a[][];
mart(){memset(a,,sizeof a);}
mart operator *(const mart & B)const{
mart C;
for(int i=;i<=ml;i++)
for(int j=;j<=ml;j++)if(a[i][j])
for(int k=;k<=ml;k++)if(B.a[j][k])
UPD(C.a[i][k],1ll*a[i][j]*B.a[j][k]%mod);
return C;
}
}A,B;
mart qp(mart a,int b){
mart c;
for(int i=;i<=ml;i++)
c.a[i][i]=;
for(;b;b>>=,a=a*a)
if(b&)c=c*a;
return c;
}
void work(){
memset(mm,,sizeof mm);
memset(A.a,,sizeof A.a);
memset(B.a,,sizeof B.a);
for(int i=;i<;i++)if(sama.ch[][i]){
mm[sama.ch[][i]][]=++tot;
p[tot]=mk(sama.ch[][i],),q.push(p[tot]);
B.a[][tot]=;
}
while(!q.empty()){
pr now=q.front();q.pop();
int x=now.fi,y=now.se,nx,ny;
for(int i=;i<;i++){
if(!sama.ch[][i]&&!samb.ch[][i])nx=,ny=;
else if(sama.ch[][i]&&!samb.ch[][i]){
if(y==)nx=sama.ch[x][i],ny=;
else nx=sama.ch[][i],ny=;
}
else if(!sama.ch[][i]&&samb.ch[][i]){
if(x==)nx=,ny=samb.ch[y][i];
else nx=,ny=samb.ch[][i];
}
else{
if(x&&y)nx=sama.ch[][i],ny=samb.ch[][i];
else if(!x&&y)nx=sama.ch[][i],ny=samb.ch[y][i];
else if(x&&!y)nx=sama.ch[x][i],ny=samb.ch[][i];
else nx=sama.ch[][i],ny=samb.ch[][i];
}
if(!nx&&!ny)continue;
if(!mm[nx][ny])mm[nx][ny]=++tot,p[tot]=mk(nx,ny),q.push(p[tot]);
A.a[mm[x][y]][mm[nx][ny]]++;
}
}
ml=tot+;
A.a[ml][ml]=;
for(int i=;i<=tot;i++)
if(p[i].se!=)A.a[i][ml]++;
B=B*qp(A,len);
printf("%d\n",B.a[][ml]);
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&len);
scanf("%s",sa+);
sama.last=sama.tot=;
memset(sama.ch,,sizeof sama.ch);
for(int i=;i<=n;i++){
if(sa[i]=='A')sa[i]='';
if(sa[i]=='T')sa[i]='';
if(sa[i]=='C')sa[i]='';
if(sa[i]=='G')sa[i]='';
sama.add(sa[i]-'');
}
scanf("%s",sb+);
samb.last=samb.tot=;
memset(samb.ch,,sizeof samb.ch);
for(int i=;i<=m;i++){
if(sb[i]=='A')sb[i]='';
if(sb[i]=='T')sb[i]='';
if(sb[i]=='C')sb[i]='';
if(sb[i]=='G')sb[i]='';
samb.add(sb[i]-'');
}
work();
}
return ;
}
fighting!fighting!
4.29
先读了一遍题,发现T1如果不是分数的话就是sb题,然后感觉分数也不是很难搞,T2一脸不可做,T3貌似50分是送的?然后开始想T1,感觉正解应该是什么单调队列优化dp之类的,但是推了一会也没推出来,然后想了个骗分,就是二分答案,对最后分出来的答案在序列上跑一遍卡个边界就可以了,然后写完了,造了几组数据调了调eps,然而T2的10分暴力还是不想写,就去看T3,先写了50分暴力,然后想正解,感觉是网络流?yy了一堆建图都失败了。之后又去想T1,乱搞出来了个$O(n^{2})$dp,然而怎么也不能优化。最后把T2的10分暴力打了就完了。100+10+50=160rank2
T1,骗分是正解?就是二分答案,然后对于最后的答案我们可以暴力找分数和他匹配,也可以像我再在序列上跑一遍。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define eps 1e-7
#define N 100500
#define ld long double
using namespace std;
int T,n;ld mx,mn;
bool vis1[N],vis2[N];
struct data{
ld l,r;
bool operator < (const data &a)const{return l<a.l;}
}d[N];
bool check(ld x){
ld now=mn;
for(int i=;i<=n;i++){
now=max(now,d[i].l);now+=x;
if(now>d[i].r)return ;
}
return ;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
struct fra{
int son,mom;
fra(int a,int b){int g=gcd(a,b);son=a/g;mom=b/g;}
fra(){son=,mom=;}
bool operator < (const fra & a)const{return 1ll*son*a.mom<1ll*mom*a.son;}
void print(){printf("%d/%d\n",son,mom);}
};
int main(){
scanf("%d",&T);
while(T--){
scanf("%d",&n);
mn=1e9,mx=;
for(int i=;i<=n;i++){cin>>d[i].l>>d[i].r;mn=min(mn,d[i].l);mx=max(mx,d[i].r);}
sort(d+,d+n+);
ld l=,r=(mx-mn)/1.0/n,mid,e=1e-;
while(l+e<=r){
mid=(l+r)/2.0;
if(check(mid))l=mid;
else r=mid;
}
ld now=mn;
memset(vis1,,sizeof vis1);
memset(vis2,,sizeof vis2);
for(int i=;i<=n;i++,now+=l){
if(now<=d[i].l+eps)vis1[i]=,now=d[i].l;
if(now+l>=d[i].r-eps)vis2[i]=;
}
fra ans=fra(,);
for(int i=,pos;i<=n;i++){
if(vis1[i])pos=i;
if(vis2[i])ans=min(ans,fra(round(d[i].r-d[pos].l),i-pos+));
}
ans.print();
}
return ;
}
T2,挺好的一道数学题。
60分dp就是定义f[i][j]表示i个点中有j棵树的方案数,然后
$$f[i][j]= \frac{1}{j} \sum_{k=1}^{i}{f[i-k][j-1] \cdot k^{k-2} \cdot C_{i}^{k}}$$
套路转移即可。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 1050
#define mod 998244353
using namespace std;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int n,m,C[N][N],g[N],f[N][N],inv[N],ans,gc[N];
int main(){
//freopen("test.in","r",stdin);
register int i,j,k;
scanf("%d%d",&n,&m);
for(i=;i<=n;++i){
C[i][]=;
for(j=;j<=i;++j)
C[i][j]=(C[i-][j-]+C[i-][j])%mod;
}
g[]=;
for(i=;i<=n;++i)g[i]=qp(i,i-);
inv[]=inv[]=;
for(i=;i<=n;++i)inv[i]=mod-1ll*(mod/i)*inv[mod%i]%mod;
f[][]=;
for(i=;i<=n;++i){
for(k=;k<=i;++k)gc[k]=1ll*g[k]*C[i][k]%mod;
for(j=;j<=i;++j){
for(k=;k<=i-j+;++k)
UPD(f[i][j],1ll*f[i-k][j-]*gc[k]%mod);
f[i][j]=1ll*f[i][j]*inv[j]%mod;
UPD(f[i][],mod-f[i][j]);
}
UPD(f[i][],qp(,1ll*i*(i-)/%(mod-)));
}
for(i=;i<=n;++i)
UPD(ans,1ll*f[n][i]*qp(i,m)%mod);
printf("%d\n",ans);
return ;
}
正解更nb,由第二类斯特林数可得$x^{k}= \sum_{i=1}^{min(x,k)}{S(k,i) \cdot C_{x}^{i} \cdot i!}$,然后我们考虑每i个树联通块,他的贡献就是他在所有方案中出现的次数乘上$S(k,i) \cdot i!$,然后我们就只需dp出i个点构成j棵树的方案数就可以了,这里的j<=k。注意上面60分的f中i个点中除了j棵树,还可能有不是树的联通块,而这里必须只有j棵树,转移类似
$$f[i][j]= \sum_{k=1}^{i}{f[i-k][j-1] \cdot k^{k-2} \cdot C_{i-1}^{k-1}}$$
之后把组合数拆开,然后用fft优化即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 33333
#define mod 998244353
using namespace std;
int n,m,up,g[N],h[N],ans,len,rev[N<<];
int fac[N],inv[N],S[][],f[][N<<],a[N<<],b[N<<];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
void ntt(int *a,int o){
register int i,j,k,dan,now,t;
for(i=;i<len;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
dan=qp(,(o==)?((mod-)/k):(mod--(mod-)/k));
for(i=;i<len;i+=k){
now=;
for(j=;j<(k>>);j++,now=1ll*now*dan%mod){
t=1ll*a[i+j+(k>>)]*now%mod;
a[i+j+(k>>)]=(a[i+j]-t+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
if(o==-){
int ny=qp(len,mod-);
for(i=;i<len;i++)a[i]=1ll*a[i]*ny%mod;
}
}
int main(){
scanf("%d%d",&n,&m);
up=min(n,m);
for(int i=;i<=m;i++){
S[i][]=;S[i][i]=;
for(int j=;j<i;j++)
S[i][j]=(S[i-][j-]+1ll*S[i-][j]*j%mod)%mod;
}
fac[]=;for(int i=;i<=n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[n]=qp(fac[n],mod-);for(int i=n;i;i--)inv[i-]=1ll*inv[i]*i%mod;
g[]=;for(int i=;i<=n;i++)g[i]=qp(i,i-);
h[]=;for(int i=;i<=n;i++)h[i]=qp(,1ll*i*(i-)/%(mod-));
for(len=;len<=*n;len<<=);
for(int i=;i<len;i++){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
f[][]=;
for(int i=;i<=up;i++){
for(int j=;j<=n;j++)b[j]=1ll*g[j]*inv[j-]%mod;
for(int j=;j<=n;j++)a[j]=1ll*f[i-][j]*inv[j]%mod;
for(int j=n+;j<len;j++)b[j]=a[j]=;b[]=;
ntt(a,);ntt(b,);
for(int j=;j<len;j++)f[i][j]=1ll*a[j]*b[j]%mod;
ntt(f[i],-);
for(int j=;j<=n;j++)f[i][j]=1ll*f[i][j]*fac[j-]%mod;
}
for(int i=,cnt;i<=up;i++){
cnt=;
for(int j=;j<=n;j++)
UPD(cnt,1ll*f[i][j]*C(n,j)%mod*h[n-j]%mod);
UPD(ans,1ll*cnt*S[m][i]%mod*fac[i]%mod);
}
printf("%d\n",ans);
return ;
}
T3,sbDP题,f[i][j][k]表示前后各放了i个数,前面i个有j个p,后面有k个p的最优解,然后我们可以发现,这样就可以避免重复,因为长度为i和n-i的条件我们可以一并考虑。然后就没了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define P 9705276
#define Q 12805858
#define N 205
#define pr pair<int,int>
#define mk make_pair
#define LL long long
using namespace std;
LL read(){
LL a=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while((ch>=''&&ch<='')||(ch=='.'))
{if(ch!='.')a=a*+(ch^);ch=getchar();}
return a;
}
int mm[N<<][N<<];LL mx;
int Tim,ans,len,nump,numq,n,f[N][N][N],ansj,ansk;
pr pre[N][N][N];
char s[N<<];
void dfs(int x,int p1,int p2){
if(!x)return ;
int prej=pre[x][p1][p2].first,prek=pre[x][p1][p2].second;
s[x]=(prej==p1)?'Q':'P';
s[len-x+]=(prek==p2)?'Q':'P';
dfs(x-,prej,prek);
}
int main(){
scanf("%d",&n);
for(int i=,y,z;i<=n;i++){
LL x=read();y=z=;
for(int j=;j<=&&x-1ll*j*P>=;j++)
if((x-1ll*j*P)%Q==){y=j;z=(x-1ll*j*P)/Q;break;}
if(x>mx)mx=x,nump=y,numq=z;
if(y||z)mm[y+z][y]++;
}
len=nump+numq;
memset(f,-0x3f,sizeof f);
f[][][]=;
for(int i=,up,np;i<=(len>>);i++){
up=min(i,nump);
for(int j=;j<=up;j++){
for(int k=;k<=up&&j+k<=nump;k++){
np=mm[i][j]+mm[len-i][nump-j];
if(j!=k)np+=mm[i][k]+mm[len-i][nump-k];
f[i][j][k]=f[i-][j][k];pre[i][j][k]=mk(j,k);
if(j&&f[i-][j-][k]>f[i][j][k])f[i][j][k]=f[i-][j-][k],pre[i][j][k]=mk(j-,k);
if(k&&f[i-][j][k-]>f[i][j][k])f[i][j][k]=f[i-][j][k-],pre[i][j][k]=mk(j,k-);
if(j&&k&&f[i-][j-][k-]>f[i][j][k])f[i][j][k]=f[i-][j-][k-],pre[i][j][k]=mk(j-,k-);
f[i][j][k]+=np;
}
}
}
if(len&){
for(int j=,k,np;j<=nump;j++){
k=nump-j;
np=mm[len+>>][j];if(j!=k)np+=mm[len+>>][k];
if(f[len>>][j][k]+np>ans){ans=f[len>>][j][k]+np;ansj=j;ansk=k;s[len+>>]='Q';}
if(j==nump)break;
k=nump-j-;
np=mm[len+>>][j+];if(j!=k)np+=mm[len+>>][k+];
if(f[len>>][j][k]+np>ans){ans=f[len>>][j][k]+np;ansj=j;ansk=k;s[len+>>]='P';}
}
}
else{
for(int j=;j<=nump;j++)if(f[len>>][j][nump-j]>ans)
ans=f[len>>][j][nump-j],ansj=j,ansk=nump-j;
}
dfs(len>>,ansj,ansk);
printf("%s\n",s+);
return ;
}
晚上回家打了场cf,abc傻逼题,d题打了个贪心,感觉很稳,然后c题没判同一层,d题贪心少跑了一遍,然后就是喜闻乐见的fst了。mdzz。
5.1
劳动节快乐!
先看T1,点分?看数据范围,暴力70,这么良心,然后写了4个那么namespace,写了一个多小时,感觉点分好像很恶心,就弃了,然后看T2,根本不可做啊,然后看T3,暴力60,然后写写写,写完想了想,发现这个才是傻逼点分,直接建出点分树然后维护个动态开点线段树就可以了,然后码码码,编译过一遍过样例,然后和暴力拍,发现inf设大了,炸int了,然后改了,之后写了T2的20分暴力,没有仔细想。70+25+100=195 rank4。
T1,可以二分+点分,也可以点分+超级钢琴。就tm我bzoj上过不去!!!为什么!!!pbds都过不去,开O3都过不去!!!
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define N 50505
using namespace std;
int n,m,size[N],mx[N],allsize,root,tot,vis[N],L,R;
int e=,head[N];
struct edge{
int v,w,next;
}ed[N<<];
void add(int u,int v,int w){
ed[e].v=v;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
}
void getroot(int x,int fa){
size[x]=;mx[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa||vis[v])continue;
getroot(v,x);
size[x]+=size[v];
mx[x]=max(mx[x],size[v]);
}
mx[x]=max(mx[x],allsize-size[x]);
if(mx[x]<mx[root])root=x;
}
int a[*N],l[*N],r[*N],maxn[*N][],pp[*N][],lg[*N];
void dfs(int x,int fa,int w){
a[++tot]=w;
l[tot]=L,r[tot]=R;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa||vis[v])continue;
dfs(v,x,w+ed[i].w);
}
}
void work(int x){
vis[x]=;
a[++tot]=;
L=tot;R=tot;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(vis[v])continue;
dfs(v,x,ed[i].w);
R=tot;
}
int now=allsize;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(vis[v])continue;
if(size[v]>size[x])allsize=now-size[x];
else allsize=size[v];
root=;getroot(v,);
work(root);
}
}
void st_init(){
for(int i=,j=,cnt=;i<=tot;i++){
if((j<<)<i)j<<=,cnt++;
lg[i]=cnt;
}
for(int i=;i<=tot;i++)maxn[i][]=a[i],pp[i][]=i;
for(int i=;i<=lg[tot];i++){
for(int j=;j+(<<i)-<=tot;j++){
if(maxn[j][i-]>maxn[j+(<<i-)][i-])maxn[j][i]=maxn[j][i-],pp[j][i]=pp[j][i-];
else maxn[j][i]=maxn[j+(<<i-)][i-],pp[j][i]=pp[j+(<<i-)][i-];
}
}
}
struct data{
int l,r,x,pos,val;
data(){}
data(int a,int b,int c){
l=a;r=b;x=c;
int k=lg[b-a+];
if(maxn[a][k]>maxn[b-(<<k)+][k])pos=pp[a][k],val=::a[c]+maxn[a][k];
else pos=pp[b-(<<k)+][k],val=::a[c]+maxn[b-(<<k)+][k];
}
bool operator < (const data &a)const{
return val<a.val;
}
};
__gnu_pbds::priority_queue<data> q;
int main(){
scanf("%d%d",&n,&m);
for(int i=,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
mx[]=n+;root=;allsize=n;
getroot(,);work(root);
st_init();
for(int i=;i<=tot;i++)if(l[i])
q.push(data(l[i],r[i],i));
for(int i=;i<=m;i++){
data now=q.top();q.pop();
printf("%d\n",now.val);
if(now.pos>now.l)q.push(data(now.l,now.pos-,now.x));
if(now.pos<now.r)q.push(data(now.pos+,now.r,now.x));
}
return ;
}
T2,魔法森林加强版,我们先把点权附到边上,然后问题转化成了最小的bs满足将b小于他的边都加进去有一个>=k的联通块,然后我们相当于要维护一个动态最小生成树,然后加完一条边更新答案时,我们从当前的边集里找b最大的,尝试删去他,如果删去他还有大于等于k的联通块,我们就删去他,否则,他就是这个a对应的最小的b,更新答案即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define inf 0x7fffffff
using namespace std;
struct Node{
Node *ch[],*fa;
int size,sm,sxu,maxn,maxid,val,id,rev;
Node();
void Rev();
void pushup();
void pushdown();
}*null=new Node(),tree[];
Node :: Node(){
ch[]=ch[]=fa=null;
size=sxu=maxid=id=rev=;
val=maxn=-inf;
}
void Node :: Rev(){
rev^=;
swap(ch[],ch[]);
}
void Node :: pushup(){
if(ch[]->maxn>ch[]->maxn)maxn=ch[]->maxn,maxid=ch[]->maxid;
else maxn=ch[]->maxn,maxid=ch[]->maxid;
if(val>maxn)maxn=val,maxid=id;
size=ch[]->size+ch[]->size+sm+sxu;
}
void Node :: pushdown(){
if(rev){
ch[]->Rev();
ch[]->Rev();
rev=;
}
}
void rotate(Node *x){
Node *y=x->fa,*z=y->fa;
int w=y->ch[]==x;
x->ch[w^]->fa=y;y->ch[w]=x->ch[w^];
y->fa=x;x->ch[w^]=y;
if(z->ch[]==y)z->ch[]=x;
if(z->ch[]==y)z->ch[]=x;
x->fa=z;
y->pushup();x->pushup();
}
bool isroot(Node *x){return x->fa->ch[]!=x&&x->fa->ch[]!=x;}
bool get(Node *x){return x->fa->ch[]==x;}
void pushdown(Node *x){
if(!isroot(x))pushdown(x->fa);
x->pushdown();
}
void splay(Node *x){
pushdown(x);
Node *y;
for(;!isroot(x);rotate(x)){
y=x->fa;
if(!isroot(y)){
if(get(y)==get(x))rotate(y);
else rotate(x);
}
}
}
void access(Node *x){
Node *y=null;
while(x!=null){
splay(x);
x->sxu+=x->ch[]->size-y->size;
x->ch[]=y;
x->pushup();
y=x;x=x->fa;
}
}
void make_root(Node *x){
access(x);
splay(x);
x->Rev();
}
void link(Node *x,Node *y){
make_root(x);
make_root(y);
x->fa=y;
y->sxu+=x->size;
y->pushup();
}
void cut(Node *x,Node *y){
make_root(x);
access(y);
splay(y);
y->ch[]=x->fa=null;
y->pushup();
}
bool check(Node *x,Node *y){
make_root(x);
access(y);
splay(y);
splay(x);
return (y->fa!=null);
}
struct data{
int u,v,id,a,b;
bool operator < (const data & x)const{
if(b==x.b)return id<x.id;
return b<x.b;
}
bool operator == (const data & x)const{
return id==x.id;
}
}d[];
bool cmpa(data x,data y){return x.a<y.a;}
struct que{
priority_queue<data> a,b;
void ins(data x){a.push(x);}
void del(data x){b.push(x);}
void mt(){while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();}
data top(){mt();return a.top();}
int size(){mt();return a.size();}
void pop(){mt();a.pop();}
}q;
int a[],b[],n,m,K,ans,cnt;
int main(){
scanf("%d%d%d",&n,&m,&K);
for(int i=;i<=n;i++)
scanf("%d%d",&a[i],&b[i]);
if(K==){
ans=inf;
for(int i=;i<=n;i++)ans=min(ans,a[i]+b[i]);
printf("%d\n",ans);
return ;
}
for(int i=;i<=m;i++){
scanf("%d%d",&d[i].u,&d[i].v);
d[i].a=max(a[d[i].u],a[d[i].v]);
d[i].b=max(b[d[i].u],b[d[i].v]);
}
sort(d+,d+m+,cmpa);
for(int i=;i<=m;i++)d[i].id=i;
for(int i=;i<=n;i++){
tree[i].ch[]=tree[i].ch[]=tree[i].fa=null;
tree[i].id=tree[i].maxid=i;
tree[i].size=tree[i].sm=;
tree[i].rev=tree[i].sxu=;
tree[i].val=tree[i].maxn=-inf;
}
for(int i=n+;i<=n+m;i++){
tree[i].ch[]=tree[i].ch[]=tree[i].fa=null;
tree[i].id=tree[i].maxid=i;
tree[i].size=tree[i].sm=;
tree[i].rev=tree[i].sxu=;
tree[i].val=tree[i].maxn=d[i-n].b;
}
cnt=;
ans=inf;
for(int i=;i<=m;i++){
int u=d[i].u,v=d[i].v;
if(!check(&tree[u],&tree[v])){
make_root(&tree[u]);if(tree[u].size>=K)cnt--;
make_root(&tree[v]);if(tree[v].size>=K)cnt--;
link(&tree[u],&tree[n+i]);
link(&tree[v],&tree[n+i]);
make_root(&tree[n+i]);if(tree[n+i].size>=K)cnt++;
q.ins(d[i]);
}
else{
make_root(&tree[u]);access(&tree[v]);splay(&tree[v]);
if(tree[v].maxn>d[i].b){
int y=tree[v].maxid;
cut(&tree[y],&tree[d[y-n].u]);
cut(&tree[y],&tree[d[y-n].v]);
link(&tree[u],&tree[n+i]);
link(&tree[v],&tree[n+i]);
q.ins(d[i]);
q.del(d[y-n]);
}
}
if(!cnt)continue;
while(q.size()){
data now=q.top();
make_root(&tree[n+now.id]);if(tree[n+now.id].size>=K)cnt--;
cut(&tree[n+now.id],&tree[now.u]);
cut(&tree[n+now.id],&tree[now.v]);
make_root(&tree[now.u]);if(tree[now.u].size>=K)cnt++;
make_root(&tree[now.v]);if(tree[now.v].size>=K)cnt++;
if(!cnt){
cnt++;
link(&tree[n+now.id],&tree[now.u]);
link(&tree[n+now.id],&tree[now.v]);
ans=min(ans,d[i].a+now.b);
break;
}
q.pop();
}
}
if(ans<inf)printf("%d\n",ans);
else puts("no solution");
return ;
}
T3,裸点分,貌似还有一系列根号做法。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100500
#define inf 0x3fffffff
using namespace std;
int root,size[N],mx[N],allsize,n,m;
int e=,head[N];
struct edge{
int v,w,next;
}ed[N<<];
void add(int u,int v,int w){
ed[e].v=v;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
}
int dep[N],val[N],fa[N][];
void dfs(int x,int d,int vv){
dep[x]=d;
val[x]=vv;
for(int i=;(<<i)<=d;i++)
fa[x][i]=fa[fa[x][i-]][i-];
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x][])continue;
fa[v][]=x;
dfs(v,d+,vv+ed[i].w);
}
}
int getlca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=;~i;i--)
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=;~i;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
int getdis(int x,int y){
return val[x]+val[y]-*val[getlca(x,y)];
}
int par[N],vis[N];
void getroot(int x,int fa){
size[x]=;mx[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa||vis[v])continue;
getroot(v,x);
size[x]+=size[v];
mx[x]=max(mx[x],size[v]);
}
mx[x]=max(mx[x],allsize-size[x]);
if(mx[x]<mx[root])root=x;
}
void work(int x){
vis[x]=;
int now=allsize;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(vis[v])continue;
if(size[v]>size[x])allsize=now-size[x];
else allsize=size[v];
root=;getroot(v,);
par[root]=x;
work(root);
}
}
int sz,rot[N],lon[N<<],ron[N<<],mn[N<<];
void update(int &rt,int l,int r,int x,int y){
if(!rt)rt=++sz,mn[rt]=inf;
mn[rt]=min(mn[rt],y);
if(l==r)return ;
int mid=(l+r)>>;
if(x<=mid)update(lon[rt],l,mid,x,y);
else update(ron[rt],mid+,r,x,y);
}
int query(int rt,int l,int r,int x,int y){
if(!rt)return inf;
if(x<=l&&r<=y)return mn[rt];
int mid=(l+r)>>;
if(y<=mid)return query(lon[rt],l,mid,x,y);
if(x>mid)return query(ron[rt],mid+,r,x,y);
return min(query(lon[rt],l,mid,x,y),query(ron[rt],mid+,r,x,y));
}
void build(){
dfs(,,);
allsize=n;root=;mx[]=n+;
getroot(,);work(root);
for(int i=;i<=n;i++)
for(int j=i;j;j=par[j])
update(rot[j],,n,i,getdis(i,j));
}
int query(int l,int r,int x){
int ans=query(rot[x],,n,l,r);
for(int i=par[x];i;i=par[i])
ans=min(ans,getdis(x,i)+query(rot[i],,n,l,r));
return ans;
}
int main(){
scanf("%d",&n);
for(int i=,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
build();
scanf("%d",&m);
int l,r,x;
while(m--){
scanf("%d%d%d",&l,&r,&x);
printf("%d\n",query(l,r,x));
}
return ;
}
我爱拜仁。
5.3
今天三道水题,T1double炸精我又输出的lf然后就爆零了,以后要特别注意这一点。
考试看完题,发现都是原题,先写T1,然后T3,最后T2,都拍了拍,觉得挺稳的,然后T1就挂了。最后还写了一个多小时的果冻运输的暴搜,下午试了试效率极低。
T1,裸的旋转卡壳。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100500
#define LL long long
using namespace std;
int n,top,tim;
LL ans;
struct point{
LL x,y;
point(){x=y=;}
point(LL a,LL b){x=a;y=b;}
point operator - (point a){return point(x-a.x,y-a.y);}
LL operator * (point a){return x*a.y-y*a.x;}
}p[N],q[N];
LL dis(point a){
return a.x*a.x+a.y*a.y;
}
bool cmp(point a,point b){
if((a-p[])*(b-p[])==)return dis(a-p[])<dis(b-p[]);
return (a-p[])*(b-p[])>;
}
void graham(){
for(int i=;i<=n;i++)
if(p[i].x<p[].x||(p[i].x==p[].x&&p[i].y<p[i].y))
swap(p[],p[i]);
sort(p+,p+n+,cmp);
top=;q[top]=p[];
for(int i=;i<=n;i++){
while(top>&&(p[i]-q[top])*(q[top]-q[top-])>=)top--;
q[++top]=p[i];
}
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%lld%lld",&p[i].x,&p[i].y);
graham();
q[top+]=q[];
for(int i=,j=,k=,l=;i<=top;i++){
while((q[i+]-q[i])*(q[j+]-q[i])>(q[i+]-q[i])*(q[j]-q[i])){
if(j==l){l++;if(l==top+)l=;}
j++;if(j==top+)j=;
}
while((q[i]-q[j])*(q[k+]-q[j])>(q[i]-q[j])*(q[k]-q[j])){k++;if(k==top+)k=;}
while((q[j]-q[i])*(q[l+]-q[i])>(q[j]-q[i])*(q[l]-q[i])){l++;if(l==top+)l=;}
ans=max(ans,(q[i]-q[j])*(q[k]-q[j])+(q[j]-q[i])*(q[l]-q[i]));
}
if(ans&)printf("%lld.5\n",ans>>);
else printf("%lld.0\n",ans>>);
return ;
}
T2,航海舰队弱化版,可以直接两种东西分开卷,也可以像我一样傻逼的按照bzoj4503一样搞一下。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 998244353
#define N 526666
using namespace std;
int n,m,q,l1,l2,tot,num,len,rev[N],ans[N];
int a[N],a2[N],one[N],b[N],c[N],bc[N],b2c[N],fin,fi,fj;
char s[];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void ntt(int *a,int o){
register int i,j,k,dan,now,t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
dan=qp(,(o==)?((mod-)/k):(mod--(mod-)/k));
for(i=;i<len;i+=k){
now=;
for(j=;j<(k>>);++j,now=1ll*now*dan%mod){
t=1ll*a[i+j+(k>>)]*now%mod;
a[i+j+(k>>)]=(a[i+j]-t+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
if(o==-){
int inv=qp(len,mod-);
for(i=;i<len;++i)a[i]=1ll*a[i]*inv%mod;
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%s",s+);
for(int j=;j<=m;j++){
if(s[j]=='G')a[tot++]=;
else a[tot++]=;
}
}
for(int i=;i<tot;i++)a2[i]=a[i]*a[i];
for(int i=;i<tot;i++)one[i]=;
for(len=;len<=(*tot);len<<=);
for(int i=;i<len;i++){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
ntt(a2,);
ntt(a,);
ntt(one,);
scanf("%d",&q);
while(q--){
scanf("%d%d",&l1,&l2);
num=;
for(int i=;i<=l1;i++){
scanf("%s",s+);
for(int j=;j<=l2;j++){
if(s[j]=='G')b[num++]=;
else b[num++]=;
c[num-]=;
}
if(i==l1)break;
for(int j=l2+;j<=m;j++){
b[num++]=;
c[num-]=;
}
}
reverse(b,b+num);
reverse(c,c+num);
for(int i=;i<num;i++){
b2c[i]=b[i]*b[i]*c[i];
bc[i]=mod-*b[i]*c[i];
}
for(int i=num;i<len;i++)bc[i]=b2c[i]=c[i]=;
ntt(c,);ntt(bc,);ntt(b2c,);
for(int i=;i<len;i++)
ans[i]=((1ll*a2[i]*c[i]%mod+1ll*a[i]*bc[i]%mod)%mod+1ll*one[i]*b2c[i]%mod)%mod;
ntt(ans,-);
fin=l1*l2+;
for(int i=;i<=n-l1+;i++)
for(int j=;j<=m-l2+;j++)
if(ans[(i-)*m+j+num-]<fin){
fin=ans[(i-)*m+j+num-];
fi=i;fj=j;
}
printf("%d %d\n",fi,fj);
}
return ;
}
T3,弗洛伊德矩阵快速幂
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define inf 0x3f3f3f3f
using namespace std;
void getmin(int &a,int b){a>b?a=b:;}
int n,m,ans,up;
struct mart{
int a[][];
mart(){memset(a,0x3f,sizeof a);}
}A[];
bool check(mart a){
for(int i=;i<=n;i++)
if(a.a[i][i]<)return ;
return ;
}
mart mul(mart a,mart b){
mart c;
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)if(a.a[i][j]<inf)
for(int k=;k<=n;k++)if(b.a[j][k]<inf)
getmin(c.a[i][k],a.a[i][j]+b.a[j][k]);
return c;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=,u,v,w;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
A[].a[u][v]=w;
}
for(int i=;i<=n;i++)A[].a[i][i]=;
for(up=;(<<up)<=n;up++)
A[up]=mul(A[up-],A[up-]);up--;
for(int i=;i<=n;i++)A[up+].a[i][i]=;
for(int i=up;~i;i--){
A[up+]=mul(A[up+],A[i]);
if(!check(A[up+]))ans+=(<<i),A[up+]=A[up+];
if(ans>n){puts("");return ;}
}
ans++;
if(ans>n)puts("");
else printf("%d\n",ans);
return ;
}
虚...
5.4
又tm炸了。考试先看T1,感觉十分不可做,写了30分暴力就弃疗了,看T2,更不可做,先写了60,然后找不着规律,推不出式子。然后看T3,辛辛苦苦打了好久,本来感觉40稳,没准还能多骗点分,然后因为评测机的种种傻逼,mle成15。没有然后了30+60+15=105 rank8
T1,kosaraju+bitset+分块+st表,kosalaju好简单啊!
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cassert>
#define N 155
#define ull unsigned long long
#define oo 0x7fffffffffffffff
using namespace std;
struct Bit{
ull a[];
void clear(){a[]=a[]=a[]=;}
int get(int x){return (a[x>>]>>(x&))&;}
void Or(int x){a[x>>]|=1ll<<(x&);}
void Xor(int x){a[x>>]^=1ll<<(x&);}
int Lowbit(){
if(a[])return __builtin_ctzll(a[]);
if(a[])return __builtin_ctzll(a[])+;
if(a[])return __builtin_ctzll(a[])+;
return -;
}
Bit operator & (Bit B){
Bit ans;
ans.a[]=a[]&B.a[];
ans.a[]=a[]&B.a[];
ans.a[]=a[]&B.a[];
return ans;
}
bool operator ! (){return !a[]&&!a[]&&!a[];}
}vis,g1[N],g2[N],st1[][][N],st2[][][N];
int n,m,mm,tot,qr,q[N],top,be[],en[],lg[],num,ans;
struct data{int u,v;}d[];
void dfs1(int x){
vis.Xor(x);
Bit now;
while(){
now=vis&g1[x];
if(!now)break;
int v=now.Lowbit();
dfs1(v);
}
q[++top]=x;
}
void dfs2(int x){
vis.Xor(x);num++;
Bit now;
while(){
now=vis&g2[x];
if(!now)break;
int v=now.Lowbit();
dfs2(v);
}
}
int main(){
scanf("%d%d%d",&n,&m,&qr);
for(int i=;i<=m;i++)scanf("%d%d",&d[i].u,&d[i].v);
mm=sqrt(m);
for(int i=;i<=m;i++){
be[i]=(i-)/mm+;
en[be[i]]=i;
st1[be[i]][][d[i].u].Or(d[i].v);
st2[be[i]][][d[i].v].Or(d[i].u);
}
tot=be[m];
for(int i=,j=,cnt=;i<=tot;i++){
if((j<<)<i)j<<=,cnt++;
lg[i]=cnt;
}
for(int i=;i<=lg[tot];i++){
for(int j=;j+(<<i)-<=tot;j++){
for(int k=;k<=n;k++){
st1[j][i][k].a[]=st1[j][i-][k].a[]|st1[j+(<<i-)][i-][k].a[];
st1[j][i][k].a[]=st1[j][i-][k].a[]|st1[j+(<<i-)][i-][k].a[];
st1[j][i][k].a[]=st1[j][i-][k].a[]|st1[j+(<<i-)][i-][k].a[];
st2[j][i][k].a[]=st2[j][i-][k].a[]|st2[j+(<<i-)][i-][k].a[];
st2[j][i][k].a[]=st2[j][i-][k].a[]|st2[j+(<<i-)][i-][k].a[];
st2[j][i][k].a[]=st2[j][i-][k].a[]|st2[j+(<<i-)][i-][k].a[];
}
}
}
int l,r;
while(qr--){
scanf("%d%d",&l,&r);
for(int i=;i<=n;i++)g1[i].clear(),g2[i].clear();
if(be[l]==be[r]){
for(int i=l;i<=r;i++){
g1[d[i].u].Or(d[i].v);
g2[d[i].v].Or(d[i].u);
}
}
else{
for(int i=l;i<=en[be[l]];i++){
g1[d[i].u].Or(d[i].v);
g2[d[i].v].Or(d[i].u);
}
for(int i=en[be[r]-]+;i<=r;i++){
g1[d[i].u].Or(d[i].v);
g2[d[i].v].Or(d[i].u);
}
if(be[l]+<be[r]){
int k=lg[be[r]-be[l]-];
for(int i=;i<=n;i++){
g1[i].a[]|=st1[be[l]+][k][i].a[]|st1[be[r]-(<<k)][k][i].a[];
g1[i].a[]|=st1[be[l]+][k][i].a[]|st1[be[r]-(<<k)][k][i].a[];
g1[i].a[]|=st1[be[l]+][k][i].a[]|st1[be[r]-(<<k)][k][i].a[];
g2[i].a[]|=st2[be[l]+][k][i].a[]|st2[be[r]-(<<k)][k][i].a[];
g2[i].a[]|=st2[be[l]+][k][i].a[]|st2[be[r]-(<<k)][k][i].a[];
g2[i].a[]|=st2[be[l]+][k][i].a[]|st2[be[r]-(<<k)][k][i].a[];
}
}
}
vis.clear();
for(int i=;i<=n;i++)vis.Or(i);
top=;
for(int i=;i<=n;i++)
if(vis.get(i))dfs1(i);
vis.clear();
for(int i=;i<=n;i++)vis.Or(i);
ans=;
for(int i=n;i;i--){
if(vis.get(q[i])){
num=;
dfs2(q[i]);
ans+=num*(num-)/;
}
}
printf("%d\n",ans);
}
}
T2,优秀的dp,f[i]表示i个点组成的有向完全强联通图的方案数。每一个有向完全图缩点后一定是一堆强联通块组成的一条链,然后1号点能走的就是他的强联通块大小以及后面的所有强联通块大小之和。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 2005
using namespace std;
int n,P,C[N][N],f[N],g[N],ans[N];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%P)
if(b&)c=1ll*c*a%P;
return c;
}
void UPD(int &a,int b){a=(a+b>=P)?(a+b-P):(a+b);}
int main(){
scanf("%d%d",&n,&P);
for(int i=;i<=n;i++){
C[i][]=;
for(int j=;j<=i;j++)
C[i][j]=(C[i-][j-]+C[i-][j])%P;
}
g[]=;
for(int i=;i<=n;i++)f[i]=g[i]=qp(,i*(i-)/);
for(int i=;i<=n;i++)
for(int j=;j<i;j++)
UPD(f[i],P-1ll*C[i][j]*f[j]%P*g[i-j]%P);
for(int i=;i<=n;i++)
for(int j=;j<=n-i;j++)
UPD(ans[i+j],1ll*f[i]*C[n-][i-]%P*g[j]%P*C[n-i][j]%P*g[n-i-j]%P);
for(int i=;i<=n;i++)printf("%d\n",ans[i]);
}
T3,对于出现次数大于$\sqrt{n}$的左端点,我们O(n)扫来统计答案,其他的暴力分块查询。v.size()贼慢。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#include <unordered_map>
#define N 150005
#define MP unordered_map<int,int>
#define pb push_back
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
char B[<<],*S=B,*T=B;
#define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
inline int read()
{
register int x=;register char c=getc;
while(c<''|c>'')c=getc;
while(c>=''&c<='')x=*x+(c^),c=getc;
return x;
}
MP pp;
vector <int> vv[N],v1[N],v2[N];
int n,m,nn,K,a[N],s[N],cnt[N],de1[N],de2[N],sum[N],vvv[N];
int tot,be[N],en[],val[N],tag[];
struct data{int l,r,w;}d[N];
inline void calc(int val){
register int i,j,u,v;
for(i=n,u=,v=;i;--i){
u+=de1[i];
cnt[i]=cnt[i+];
if(s[i]==(val^K))cnt[i]++,v+=u;
for(j=;j<v1[i+].size();++j)
v-=(cnt[i+]-cnt[d[v1[i+][j]].r+])*d[v1[i+][j]].w;
if(s[i-]==val)sum[i]+=v;
}
for(i=,u=,v=;i<=n;++i){
u+=de2[i];
cnt[i]=cnt[i-];
if(s[i-]==val)cnt[i]++,v+=u;
for(j=;j<v2[i-].size();++j)
v-=(cnt[i-]-cnt[d[v2[i-][j]].l-])*d[v2[i-][j]].w;
if(s[i]==(val^K))sum[i+]-=v;
}
}
int main(){
//freopen("test.in","r",stdin);
//freopen("3.out","w",stdout);
register int i,j,k,l,now,pos,sz;
n=read();m=read();K=read();
nn=;
for(i=;i<=n;++i){
a[i]=read();
s[i]=s[i-]^a[i];
if(!pp[s[i-]])pp[s[i-]]=++tot,vvv[tot]=s[i-];
vv[pp[s[i-]]].pb(i);
be[i]=(i-)/nn+;
en[be[i]]=i;
}
for(i=;i<=m;++i){
d[i].l=read();d[i].r=read();d[i].w=read();
de1[d[i].r]+=d[i].w;de1[d[i].l-]-=d[i].w;
de2[d[i].l]+=d[i].w;de2[d[i].r+]-=d[i].w;
v1[d[i].l].pb(i);v2[d[i].r].pb(i);
}
for(i=;i<=tot;++i)
if(vv[i].size()>=nn)calc(vvv[i]);
for(i=n;i;--i){
sz=v2[i].size();
for(j=;j<sz;++j){
l=d[v2[i][j]].l;
pos=min(en[be[l]],i);
if(pos-l+>l-en[be[l]-]){
for(k=en[be[l]-]+;k<l;++k)val[k]-=d[v2[i][j]].w;
tag[be[l]]+=d[v2[i][j]].w;
}
else{
for(k=l;k<=pos;++k)val[k]+=d[v2[i][j]].w;
}
for(k=be[l]+;k<=be[i];++k)tag[k]+=d[v2[i][j]].w;
}
sz=vv[pp[s[i]^K]].size();
if(sz<nn){
now=;
for(j=;j<sz;++j){
pos=vv[pp[s[i]^K]][j];
if(pos>i)break;
sum[pos]+=val[pos]+tag[be[pos]];
now+=val[pos]+tag[be[pos]];
}
sum[i+]-=now;
}
}
for(i=,now=;i<=n;++i){
now=now+sum[i];
printf("%d ",now&());
}puts("");
return ;
}
5.5
考的是jsoi round2 day2 被暴虐。
先看T1,计算几何,凸包求交,半平面交有70?然后看T2,50送的?T3,主席树裸题?然后先去写T2,写了20暴力,发现方案数不是很好搞,然后找了找规律,发现可以暴力枚举第一个块的长宽,然后组合数什么的乱搞一下,推完打完快2h了,然后赶紧去看T3,只会log2?不管了,先写吧,写完发现二分的log可以直接在线段树上跑,然后就去掉了,改了改拍上了,极限数据大概要跑3s左右,虚。又卡了卡常,没啥用,然后就剩30min左右了,赶紧去看T1,写了一半感觉自己写不出来半平面交了,刚打算弃辽,然后教练说延长15min,瞬间不虚,推了推式子,倒是一遍过样例了,之后也没啥时间了,就没测大点。40+50+100=190 rank1,T1被卡log了。貌似在js刚进前十?菜。
T1,闵科夫斯基和,没听说过。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100050
#define LL long long
using namespace std;
int n,m,qr,top,tot;
struct point{
int x,y;
point(){x=y=;}
point(int a,int b){x=a;y=b;}
point operator + (point a){return point(x+a.x,y+a.y);}
point operator - (point a){return point(x-a.x,y-a.y);}
LL operator * (point a){return 1ll*x*a.y-1ll*y*a.x;}
}p1[N],p2[N],p[N],q[N],B;
LL getdis(point a){
return sqrt(1ll*a.x*a.x+1ll*a.y*a.y);
}
bool cmpp(point a,point b){
LL now=(a-B)*(b-B);
if(now==)return getdis(a-B)<getdis(b-B);
return now>;
}
void graham(point *a,int &n){
for(int i=;i<=n;i++)
if(a[i].x<a[].x||(a[i].x==a[].x&&a[i].y<a[].y))
swap(a[],a[i]);
B=a[];
sort(a+,a+n+,cmpp);
top=;q[]=a[];
for(int i=;i<=n;i++){
while(top>&&(a[i]-q[top])*(q[top]-q[top-])>=)top--;
q[++top]=a[i];
}
for(int i=;i<=top;i++)a[i]=q[i];
n=top;
} struct line{
point v;
double k;
line(){}
line(point a,point b){
v=b-a;
k=atan2(v.y,v.x);
}
}l[N];
#define _P -(acos(-1.0)/2.0)
bool cmpl(line a,line b){
if(a.k==_P)return ;
if(b.k==_P)return ;
if(a.k>_P&&b.k>_P)return a.k<b.k;
if(a.k<_P&&b.k<_P)return a.k<b.k;
return a.k>_P;
} int main(){
scanf("%d%d%d",&n,&m,&qr);
for(int i=;i<=n;i++)scanf("%d%d",&p1[i].x,&p1[i].y);
for(int i=;i<=m;i++){
scanf("%d%d",&p2[i].x,&p2[i].y);
p2[i].x*=-;p2[i].y*=-;
}
graham(p1,n);
graham(p2,m);
for(int i=;i<n;i++)l[i]=line(p1[i],p1[i+]);
l[n]=line(p1[n],p1[]);
for(int i=;i<m;i++)l[n+i]=line(p2[i],p2[i+]);
l[n+m]=line(p2[m],p2[]);
sort(l+,l+n+m+,cmpl);
p[]=p1[]+p2[];
for(int i=;i<n+m;i++)p[i+]=p[i]+l[i].v;
tot=n+m;
graham(p,tot);
point now;
while(qr--){
int l=,r=tot,mid,fin=;
scanf("%d%d",&now.x,&now.y);
while(l<=r){
mid=(l+r)>>;
if((p[mid]-p[])*(now-p[])>=)fin=mid,l=mid+;
else r=mid-;
}
if(fin==tot||fin==){puts("");continue;}
if((now-p[fin])*(p[fin+]-p[fin])>)puts("");
else puts("");
}
}
T2倒是一道不错的题目,我们发现每走一步,相当于限制了lcm(n,m)步,因为如果(1,1)走到了(1,2),那么(n,2)就只能走到(n,3)。然后我们现在就只需走gcd步就能得到对应的遍历全图的方案,于是我们设第一个块竖着走了x步,横着走了y步,我们发现一个方案是合法的,当且仅当gcd(x,n)=1&&gcd(y,m)=1,这个是因为要遍历n和m的全部剩余系。然后我们就可以枚举x,然后对于每个块dp在这个块里撞到障碍的方案数,因为每个块走的是一样的,所以还要保证在之前的所有块中这种走法是合理的。时间复杂度$O(nm\frac{gcd(n,m)}{4})$,常数极小。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 998244353
#define N 55
using namespace std;
int T,n,m,l,g,ans,C[N<<][N<<],blo[N][N],f1[N][N],f2[N][N],vis[N][N];
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
char s[N];
int main(){
scanf("%d",&T);
for(int i=;i<=;i++){
C[i][]=;
for(int j=;j<=i;j++)
C[i][j]=(C[i-][j-]+C[i-][j])%mod;
}
while(T--){
scanf("%d%d",&n,&m);
for(int i=;i<n;i++){
scanf("%s",s);
for(int j=;j<m;j++)
blo[i][j]=s[j]-'';
}
g=gcd(n,m);
l=n*m/g;
ans=;
for(int x=,y,nx,ny,rest;x<=g;x++){
if(gcd(x,n)!=||gcd(g-x,m)!=)continue;
y=g-x;
nx=ny=;
memset(vis,,sizeof vis);
memset(f1,,sizeof f1);
memset(f2,,sizeof f2);
for(int i=;i<=x;i++){
f1[i][]=;
for(int j=;j<=y;j++)f1[i][j]=(f1[i][j-]+f1[i-][j])%mod;
}
for(int i=x;~i;i--){
f2[i][y]=;
for(int j=y-;~j;j--)f2[i][j]=(f2[i][j+]+f2[i+][j])%mod;
}
rest=C[x+y][x];
for(int t=;t<=l;t++){
for(int i=;i<=x;i++){
for(int j=;j<=y;j++){
if(j==){
if(i==)f1[i][j]=;
else f1[i][j]=f1[i-][j];
}
else{
if(i==)f1[i][j]=f1[i][j-];
else f1[i][j]=(f1[i-][j]+f1[i][j-])%mod;
}
if(vis[i][j])f1[i][j]=;
if(blo[(nx+i)%n][(ny+j)%m]){
UPD(ans,1ll*f1[i][j]*f2[i][j]%mod*((t-)*g+i+j)%mod);
UPD(rest,mod-1ll*f1[i][j]*f2[i][j]%mod);
vis[i][j]=;f1[i][j]=;
}
}
}
if(!rest)break;
for(int i=x;~i;i--){
for(int j=y;~j;j--){
if(j==y){
if(i==x)f2[i][j]=;
else f2[i][j]=f2[i+][j];
}
else{
if(i==x)f2[i][j]=f2[i][j+];
else f2[i][j]=(f2[i+][j]+f2[i][j+])%mod;
}
if(vis[i][j])f2[i][j]=;
}
}
nx=(nx+x)%n;
ny=(ny+y)%m;
}
}
printf("%d\n",ans);
}
return ;
}
T3,傻逼主席树。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 500500
#define maxn 2000000
#define LL long long
using namespace std;
char B[<<],*S=B,*T=B;
#define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
inline int read()
{
register int x=;register char c=getc;
while(c<''|c>'')c=getc;
while(c>=''&c<='')x=*x+(c^),c=getc;
return x;
}
int n,m;
int sz,root[N],lon[N<<],ron[N<<],s1[N<<];
LL s2[N<<];
void insert(int &rt,int o,int l,int r,int x){
rt=++sz;
lon[rt]=lon[o],ron[rt]=ron[o];
s1[rt]=s1[o]+;s2[rt]=s2[o]+x;
if(l==r)return ;
int mid=(l+r)>>;
if(x<=mid)insert(lon[rt],lon[o],l,mid,x);
else insert(ron[rt],ron[o],mid+,r,x);
}
void query(int rt,int o,int l,int r,int x,int y,int &num,LL &sum){
if(x<=l&&r<=y){num+=s1[rt]-s1[o];sum+=s2[rt]-s2[o];return;}
int mid=(l+r)>>;
if(x<=mid)query(lon[rt],lon[o],l,mid,x,y,num,sum);
if(y>mid)query(ron[rt],ron[o],mid+,r,x,y,num,sum);
}
int getpos(int rt,int o,int l,int r,int x,int &sum){
if(l==r)return l;
int mid=(l+r)>>;
if(mid<x||(sum+s1[lon[rt]]-s1[lon[o]]>=mid-x+)){
sum+=s1[lon[rt]]-s1[lon[o]];
return getpos(ron[rt],ron[o],mid+,r,x,sum);
}
else return getpos(lon[rt],lon[o],l,mid,x,sum);
}
int main(){
n=read();m=read();
for(int i=,x;i<=n;i++){
x=read();
insert(root[i],root[i-],,maxn,x);
}
int l,r,k,num1,num2,pos,cnt;
LL ans,sum1,sum2;
while(m--){
l=read();r=read();k=read();
ans=;
num1=sum1=num2=sum2=;
if(k>){
query(root[r],root[l-],,maxn,,k-,num1,sum1);
ans+=1ll*num1*k-sum1+1ll*(num1-)*num1/;
}
if(!num1)pos=k-;
else{
cnt=;
pos=getpos(root[r],root[l-],,maxn,k,cnt)-;
}
if(k<=pos){
query(root[r],root[l-],,maxn,k,pos,num2,sum2);
ans+=1ll*pos*num2-sum2-1ll*(num2-)*num2/;
num2=sum2=;
}
query(root[r],root[l-],,maxn,pos+,maxn,num2,sum2);
ans+=sum2-1ll*num2*(pos+)-1ll*(num2-)*num2/;
printf("%lld\n",ans);
}
return ;
}
加油加油,gaygay!
CTSC Day1
考试鸽了半小时。。先看T1,我不会??!血量什么的可以O(m)维护,那么查询呢?对于每个人单独算贡献,于是我们要算除去他之外的人有i个人存活的概率,貌似O(n3)可以过70分,然后写写写,写完过了样例,没想着优化,赶紧去看T2,又是猫?又是树上lca乱搞?wc那题没改啊,gg了!先写了暴力分,然后脑子里突然想错题意了,yy了好久还是失败了。赶紧去看T3,感觉暴搜并没有那么多分,但还是写了。GG,70+40+25=135,T2这个照着错误的题意想的事好像干了好多遍了,以后一定要注意。
5.8自己考了一场,全程不在状态,就写了点暴力和骗分。貌似三道题都很吊??
CTSC Day2
这次倒是准时进场了。先看T1,裸的log2二分主席树,写完没毛病,过了大样例,还是不放心,写了个暴力对拍。然后看T2,一点不会。看T3,题答,然后开始搞,先搜过了前两个点,然后第三个点加了好多减枝才搜过去,后来又搞出来了第四个点和第七个点,然后快没时间了,吧T2的0分暴力写了好久,还加了一堆减枝,希望有分,最后写了T3第五个点的dp发现只有3分,我也很无奈啊。最后100+0+58=158,貌似第六个点可以直接贪心。GG。
5.10又考了一场,全场写T1,卡T1,写暴力,没了。
APIO
先把题目都读了一遍,发现果然都不可做的样子,然后先开T3,打算先写树的部分分,然后写了一个多小时,交了若干遍才发现图有可能不联通。然后赶紧改了拿到了树的分,发现链的gg了?好吧,可能有环,又写了,然后去写T2,本来以为有不少分都是可以写的,后来发现是我想多了。之后又写了T1的暴力。最后又是照着错误的题意去想T1,好不容易写完了,发现题意又想错了,最后发现T3的仙人掌部分分很好拿的样子,然而最后也没写完。
12+19+36=67 大概是这个分数吧。
打了两个ag回来,还是很不甘心的,尤其两场考试中都有很长的时间在想错误的题意,我也不知道我是怎么了,大赛经验啊。。。
稳住,我们能赢。
5.16
今天考试总体来说状态不够好。今天身体不是很舒服,考试的时候头也疼,还特别困。
说考试吧,看到三道题,并没有发现T2是原题,于是先写了个点双,后来发现不对,改成强联通分量,然后写网络流加反向inf边的时候才想起来这道题原来做过,于是又写了个不缩点的,两边拍上了,感觉不错,这时候大概90min了。然后看T1,觉得是一个很恶心的数据结构,又去看T3,数学或是dp的一个计数题,但是感觉怎么写都需要当前填数的状态,于是写了暴力弃掉了,这时候大概还有不到2h。之后去把T1的暴力写了,之后yy了一个随机数据跑的很快的骗分,写上了,两边也拍上了,这时候差不多就结束了。最后40+70+50=160,T1骗分就多了10分,T2还是不清楚题的本质,只记得要加反向边,却没有深入思考原因,于是gg,T3暴力多跑了10分,貌似都是50。
关于T3的一点思考:就像题解里说的:“对排列计数,不可能从左到右填数。”因为一旦这样,当前所用数的状态就是不能避免需要记录的了,所以我们需要宏观地来考虑整个过程,比如从小到大来填,这时就需要记录一些必要的信息:分成了几段,两端点的状态。但是至少需要的混乱度我觉得还是比较巧妙或者说是我想不到的,但是这个的确也是必要的,还是做题少吧,多做题,多思考。。
T1:对于序列维护一个线段树,支持区间减,求最小值,然后对于左右端点分别维护一个并查集,每个并查集维护左(右)端点相同的区间,每次删掉一个区间就在并查集里切掉一些,然后将他们合并起来,因为每切一个就会合并起来,所以这个的复杂度是O(n)的,线段树是log的没有问题。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 300500
using namespace std;
int n,m,fa1[N],fa2[N],lp[N],rp[N],ll1[N],rr1[N],ll2[N],rr2[N];
int minn[N<<],lazy[N<<],pos[N<<];
int find1(int x){return x==fa1[x]?x:fa1[x]=find1(fa1[x]);}
int find2(int x){return x==fa2[x]?x:fa2[x]=find2(fa2[x]);}
void change(int rt,int x){
lazy[rt]+=x;
minn[rt]-=x;
}
void pushup(int rt){
if(minn[rt<<]<=minn[rt<<|])minn[rt]=minn[rt<<],pos[rt]=pos[rt<<];
else minn[rt]=minn[rt<<|],pos[rt]=pos[rt<<|];
}
void pushdown(int rt){
if(lazy[rt]){
change(rt<<,lazy[rt]);
change(rt<<|,lazy[rt]);
lazy[rt]=;
}
}
void build(int rt,int l,int r){
if(l==r){
minn[rt]=rp[l]-lp[l];
pos[rt]=l;
return ;
}
int mid=(l+r)>>;
build(rt<<,l,mid);
build(rt<<|,mid+,r);
pushup(rt);
}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){
change(rt,z);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
pushup(rt);
}
void del(int rt,int l,int r,int x){
if(l==r){
minn[rt]=0x7fffffff;
pos[rt]=;
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)del(rt<<,l,mid,x);
else del(rt<<|,mid+,r,x);
pushup(rt);
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++){
scanf("%d%d",&lp[i],&rp[i]);
fa1[i]=fa2[i]=i;
ll1[i]=rr1[i]=i;
ll2[i]=rr2[i]=i;
}
build(,,n);
for(int i=;i<=n;i++){
int p=pos[];
printf("%d\n",p);
int l=lp[find1(p)],r=rp[find2(p)];
for(int j=p+;j<=n&&lp[find1(j)]<r;j=rr1[find1(j)]+){
update(,,n,ll1[find1(j)],rr1[find1(j)],r-lp[find1(j)]);
int nr=rr1[find1(j)];
fa1[find1(j)]=find1(p+);
ll1[find1(j)]=p+;rr1[find1(j)]=nr;
lp[find1(j)]=r;
}
for(int j=p-;j&&rp[find2(j)]>l;j=ll2[find2(j)]-){
update(,,n,ll2[find2(j)],rr2[find2(j)],rp[find2(j)]-l);
int nl=ll2[find2(j)];
fa2[find2(j)]=find2(p-);
rr2[find2(j)]=p-;ll2[find2(j)]=nl;
rp[find2(j)]=l;
}
del(,,n,p);
}
return ;
}
T2,网络流,这里要注意加反向边的时候一定要保证这条边能被到达。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#define inf 2333333333333ll
#define N 102
#define LL long long
using namespace std;
int q[N],he,ta,n,m;
int e=,head[N];
struct edge{
int u,v,next;
LL f;
}ed[];
void add(int u,int v,LL f){
ed[e].u=u;ed[e].v=v;ed[e].f=f;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;
ed[e].next=head[v];head[v]=e++;
}
int dep[N],S,T;
bool bfs(){
memset(dep,,sizeof dep);
dep[S]=;he=ta=;q[]=S;
while(he<=ta){
int x=q[he++];
for(int i=head[x];i;i=ed[i].next){
if(ed[i].f&&!dep[ed[i].v]){
dep[ed[i].v]=dep[x]+;
if(ed[i].v==T)return ;
q[++ta]=ed[i].v;
}
}
}
return ;
}
LL dfs(int x,LL f){
if(x==T||!f)return f;
LL ans=;
for(int i=head[x];i;i=ed[i].next){
if(ed[i].f&&dep[ed[i].v]==dep[x]+){
LL nxt=dfs(ed[i].v,min(f,ed[i].f));
ans+=nxt,f-=nxt,ed[i].f-=nxt,ed[i^].f+=nxt;
if(!f)break;
}
}
if(!ans)dep[x]=-;
return ans;
}
LL dinic(){
LL ans=;
while(bfs())ans+=dfs(S,inf);
if(!ans||ans>=inf)return -;
return ans;
}
bool vis[N];
void dfs(int x){
vis[x]=;
for(int i=head[x];i;i=ed[i].next)if(!(i&)){
ed[i^].f=inf;
if(!vis[ed[i].v])dfs(ed[i].v);
}
}
int main(){
scanf("%d%d",&n,&m);
LL w;
for(int i=,u,v;i<=m;i++){
scanf("%d%d%lld",&u,&v,&w);
add(++u,++v,w);
}
S=;T=n;
dfs(S);
printf("%lld\n",dinic());
return ;
}
T3,很优秀的一个dp,我们考虑从小到大填,需要记录的有当前的段数,两端点的状态以及把它填平需要的混乱度。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
using namespace std;
int n,m,ans,a[],f[][][][];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int main(){
scanf("%d%d",&n,&m);
if(n==){
puts("");
return ;
}
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
sort(a+,a+n+);
f[][][][]=;
f[][][][]=;
int ii=;
for(int i=;i<=n;i++){
ii^=;
memset(f[ii],,sizeof f[ii]);
for(int j=;j<i;j++){
for(int k=;k<=m;k++){
for(int l=;l<=;l++){
int w=k+(a[i]-a[i-])*(*j-l),ff=f[ii^][j][k][l];
if(!ff)continue;
if(w>m)continue;
if(l<){
UPD(f[ii][j][w][l+],1ll*ff*(-l)%mod);
UPD(f[ii][j+][w][l+],1ll*ff*(-l)%mod);
}
UPD(f[ii][j-][w][l],1ll*ff*(j-)%mod);
UPD(f[ii][j+][w][l],1ll*ff*(j-+(-l))%mod);
UPD(f[ii][j][w][l],1ll*ff*(*j-l)%mod);
}
}
}
}
for(int i=;i<=m;i++)UPD(ans,f[ii][][i][]);
printf("%d\n",ans);
return ;
}
加油!!!
5.17
pku的acm模拟赛
先看A,字符串匹配的题,要求是反回文,有点意思,好像可做。然后看B,一看就是数据结构码农题,弃弃弃。C是个数学题?然后发现D是个签到题,赶紧水了过去,E是个期望?F是个树形dp?G是个dp或网络流?然后在我决定做哪个题的时候,发现lc C题A了??赶紧去看,写了个复杂度不对的暴力,交,A了???然后发现一堆人都水过去了,0ms,0kb?我猜没有测试点,然后加上测试点之后,hzoi全员fst,那不行啊,想,死磕,发现这个和缩进优化一样直接处理个前缀和就行了,然后写,wa?没加case,加上A了,然后去想G的dp,写了写发现不可写,然后又去想F,状态定义也不太清楚,又推了推E的式子,挺像fft的,但是模数不对,复杂度也不对,就没接着搞,最后又去想了想A,也没想出来。
两道题,不够啊。。
A,对于每个串,我们首先把原串和反着的反串插进AC自动机,如001就插入001和011,然后还要再处理一个状态就是中间如果出现这个串原串也能匹配上,001的话我们还要插入00,这样直接在AC自动机上dp就可以了,最后一步要特殊转移。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 333
#define mod 998244353
using namespace std;
char s[];
int T,n,m,ans,root,sz,q[N],he,ta;
int ch[N][],fail[N],st1[N],st2[N],f[][N][<<|];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
void insert(char *s,int id){
int len=strlen(s);
int now;
for(int i=;len-i>=i;i++){
bool flag=;
for(int j=i-;~j;j--)
if(s[j]==s[(i)+(i--j)]){flag=;break;}
if(flag){
now=root;
for(int j=len-;j>=i;j--){
int t=(s[j]-'')^;
if(!ch[now][t])ch[now][t]=++sz;
now=ch[now][t];
}
st2[now]|=(<<id-);
if(!i)st1[now]|=(<<id-);
}
}
for(int i=len-;len--i<i+;i--){
bool flag=;
for(int j=i+;j<len;j++)
if(s[j]==s[(i)-(j-i-)]){flag=;break;}
if(flag){
now=root;
for(int j=;j<=i;j++){
int t=s[j]-'';
if(!ch[now][t])ch[now][t]=++sz;
now=ch[now][t];
}
st2[now]|=(<<id-);
if(i==len-)st1[now]|=(<<id-);
}
}
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
root=sz=;
memset(ch,,sizeof ch);
memset(st1,,sizeof st1);
memset(st2,,sizeof st2);
for(int i=;i<=n;i++){
scanf("%s",s);
insert(s,i);
}
ch[][]=ch[][]=;
he=ta=;q[]=;fail[]=;
while(he<=ta){
int x=q[he++];
for(int i=;i<;i++){
if(ch[x][i]){
fail[ch[x][i]]=ch[fail[x]][i];
q[++ta]=ch[x][i];
}
else ch[x][i]=ch[fail[x]][i];
}
st1[x]|=st1[fail[x]];
st2[x]|=st2[fail[x]];
}
memset(f,,sizeof f);
f[][][]=;
for(int i=;i<m;i++){
for(int j=;j<=sz;j++){
for(int k=;k<(<<n);k++)if(f[i-][j][k]){
UPD(f[i][ch[j][]][k|st1[ch[j][]]],f[i-][j][k]);
UPD(f[i][ch[j][]][k|st1[ch[j][]]],f[i-][j][k]);
}
}
}
for(int i=;i<=sz;i++){
for(int j=;j<(<<n);j++)if(f[m-][i][j]){
UPD(f[m][ch[i][]][j|st1[ch[i][]]|st2[ch[i][]]],f[m-][i][j]);
UPD(f[m][ch[i][]][j|st1[ch[i][]]|st2[ch[i][]]],f[m-][i][j]);
}
}
ans=;
for(int i=;i<=sz;i++)if(f[m][i][(<<n)-])
UPD(ans,f[m][i][(<<n)-]);
printf("%d\n",ans);
}
return ;
}
B,因为一个可能重复拼在一起,所以我们要可持久化平衡树,但是我们还是没法快速把一个区间复制多次,于是我们考虑快速幂(倍增),注意要回收空间。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 200500
#define LL long long
using namespace std;
int n,m,a[N],tot,cnt;
#define tp pair<Treap*,Treap*>
struct Treap{
Treap *ch[];
int size,val;
LL sum;
void pushup(){
sum=ch[]->sum+ch[]->sum+val;
size=ch[]->size+ch[]->size+;
}
}*null,*org,*root,mem[N<<];
Treap * newTreap(int v){
Treap *rt=mem+(++tot);
rt->ch[]=rt->ch[]=null;
rt->size=;rt->val=rt->sum=v;
}
Treap * copy(Treap *x){
Treap *rt=mem+(++tot);
*rt=*x;return rt;
}
tp split(Treap *o,int k){
if(o==null)return tp(null,null);
Treap *rt=copy(o);
tp x;
if(o->ch[]->size>=k){
x=split(rt->ch[],k);
rt->ch[]=x.second;
rt->pushup();
x.second=rt;
}
else{
x=split(rt->ch[],k-o->ch[]->size-);
rt->ch[]=x.first;
rt->pushup();
x.first=rt;
}
return x;
}
Treap * merge(Treap *a,Treap *b){
if(a==null)return b;
if(b==null)return a;
if(rand()%(a->size+b->size)<a->size){
Treap *rt=copy(a);
rt->ch[]=merge(rt->ch[],b);
rt->pushup();
return rt;
}
else{
Treap *rt=copy(b);
rt->ch[]=merge(a,rt->ch[]);
rt->pushup();
return rt;
}
}
void travel(Treap *rt){
if(rt==null)return ;
travel(rt->ch[]);
a[++cnt]=rt->val;
travel(rt->ch[]);
}
Treap * build(int l,int r){
if(l>r)return null;
int mid=(l+r)>>;
Treap *rt=newTreap(a[mid]);
rt->ch[]=build(l,mid-);
rt->ch[]=build(mid+,r);
rt->pushup();
return rt;
}
void rebuild(){
tot=n;cnt=;
travel(root);
root=build(,n);
}
LL query(int x){
Treap *now=root;
LL ans=;
while(){
if(now==null)return ans;
if(now->ch[]->size>=x)now=now->ch[];
else ans+=now->ch[]->sum+now->val,x-=now->ch[]->size+,now=now->ch[];
}
}
Treap * qp (Treap *a,int b){
Treap *c=null;
for(;b;b>>=,a=merge(a,a))
if(b&)c=merge(c,a);
return c;
}
void work(int l,int r){
int k;scanf("%d",&k);
tp x=split(root,l-);
tp y=split(x.second,r-l+);
tp z=split(x.first,l-k-);
tp w=split(z.second,(r-l+)%k);
Treap *rt=qp(z.second,(r-l+)/k);
rt=merge(rt,w.first);
root=merge(merge(x.first,rt),y.second);
if((N<<)-tot<)rebuild();
}
void update(int l,int r){
tp x=split(root,l-);
tp y=split(x.second,r-l+);
tp z=split(org,l-);
tp w=split(z.second,r-l+);
root=merge(merge(x.first,w.first),y.second);
if((N<<)-tot<)rebuild();
}
int main(){
null=mem;
null->ch[]=null->ch[]=null;
null->size=null->val=null->sum=;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
root=org=build(,n);
int o,l,r;
while(m--){
scanf("%d%d%d",&o,&l,&r);
if(o==)printf("%lld\n",query(r)-query(l-));
if(o==)work(l,r);
if(o==)update(l,r);
}
return ;
}
C,我们要的是$\sum_{i=1}^{mx}{(-\mu{(i)}) \cdot \prod_{j=1}^{n}{ \lfloor {\frac{a_{j}}{i}} \rfloor}}$ ,直接预处理前缀和然后调和级数。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 100500
using namespace std;
int mu[N],prime[N],vis[N],tot;
int T,n,ans,mn,mx,s[N],a[N];
void init(){
mu[]=;
for(int i=;i<=;i++){
if(!vis[i]){
prime[++tot]=i;
mu[i]=-;
}
for(int j=;j<=tot&&i*prime[j]<=;j++){
vis[i*prime[j]]=;
if(i%prime[j]==){
mu[i*prime[j]]=;
break;
}
mu[i*prime[j]]=-mu[i];
}
}
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int main(){
init();
scanf("%d",&T);
for(int ca=;ca<=T;ca++){
scanf("%d",&n);
mn=0x3fffffff;mx=;
memset(s,,sizeof s);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
mn=min(mn,a[i]);
mx=max(mx,a[i]);
s[a[i]]++;
}
for(int i=;i<=mx;i++)s[i]=s[i-]+s[i];
ans=;
for(int i=,now,j;i<=mn;i++)if(mu[i]){
for(now=j=;j*i<=mx;j++)
now=1ll*now*qp(j,s[min(mx,(j+)*i-)]-s[j*i-])%mod;
if(mu[i]<)UPD(ans,now);
else UPD(ans,mod-now);
}
printf("Case #%d: %d\n",ca,ans);
}
return ;
}
D,签到题,乱判就行。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int T,bo[],x[];
char s[][];
int getnum(int x){
bo[]=(s[][x+]=='X');
bo[]=(s[][x]=='X');
bo[]=(s[][x+]=='X');
bo[]=(s[][x+]=='X');
bo[]=(s[][x]=='X');
bo[]=(s[][x+]=='X');
bo[]=(s[][x+]=='X');
if((bo[])&&(bo[])&&(bo[])&&(!bo[])&&(bo[])&&(bo[])&&(bo[]))return ;
if((!bo[])&&(!bo[])&&(bo[])&&(!bo[])&&(!bo[])&&(bo[])&&(!bo[]))return ;
if((bo[])&&(!bo[])&&(bo[])&&(bo[])&&(bo[])&&(!bo[])&&(bo[]))return ;
if((bo[])&&(!bo[])&&(bo[])&&(bo[])&&(!bo[])&&(bo[])&&(bo[]))return ;
if((!bo[])&&(bo[])&&(bo[])&&(bo[])&&(!bo[])&&(bo[])&&(!bo[]))return ;
if((bo[])&&(bo[])&&(!bo[])&&(bo[])&&(!bo[])&&(bo[])&&(bo[]))return ;
if((bo[])&&(bo[])&&(!bo[])&&(bo[])&&(bo[])&&(bo[])&&(bo[]))return ;
if((bo[])&&(!bo[])&&(bo[])&&(!bo[])&&(!bo[])&&(bo[])&&(!bo[]))return ;
if((bo[])&&(bo[])&&(bo[])&&(bo[])&&(bo[])&&(bo[])&&(bo[]))return ;
if((bo[])&&(bo[])&&(bo[])&&(bo[])&&(!bo[])&&(bo[])&&(bo[]))return ;
}
int main(){
scanf("%d",&T);
while(T--){
for(int i=;i<=;i++)
scanf("%s",s[i]);
x[]=getnum();
x[]=getnum();
x[]=getnum();
x[]=getnum();
printf("%d%d:%d%d\n",x[],x[],x[],x[]);
}
}
E,化完式子是$3^{n} \cdot {\sum_{d=1}^{n} { \phi{(d)} \sum_{i=1}^{ \lfloor {\frac{n}{d}} \rfloor} } \sum_{j=1}^{ \lfloor {\frac{n}{d}} \rfloor - i} C_{n}^{id} \cdot C_{n-id}^{jd} }$
然后就可以愉快的任意模数FFT了,代码咕咕咕了555。对于任意模数fft可以看这里
F题就是一个树形dp,但是状态的确很难想到,关键的一维0和1代表当前点是否一定在这个最大匹配里,别的套路转移就好,转移时还要考虑这条边是否删去。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 998244353
#define N 50005
using namespace std;
int e,head[N];
struct edge{
int v,next;
}ed[N<<];
void add(int u,int v){
ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
int T,n,m,size[N],f[N][][],g[][];
void dfs(int x,int fa){
size[x]=;
f[x][][]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
memset(g,,sizeof g);
for(int j=;j<=size[x];j++){
if(!f[x][j][]&&!f[x][j][])continue;
for(int k=;k<=size[v];k++){
UPD(g[j+k][],1ll*f[x][j][]*f[v][k][]%mod);
UPD(g[j+k+][],1ll*f[x][j][]*f[v][k][]%mod);
UPD(g[j+k][],2ll*f[x][j][]*f[v][k][]%mod);
UPD(g[j+k][],2ll*f[x][j][]*(f[v][k][]+f[v][k][])%mod);
}
}
size[x]=min(m-,size[x]+size[v]);
for(int j=;j<=size[x];j++){
f[x][j][]=(g[j][]+g[j+m][])%mod;
f[x][j][]=(g[j][]+g[j+m][])%mod;
}
}
}
int main(){
scanf("%d",&T);
while(T--){
e=;memset(head,,sizeof head);
scanf("%d%d",&n,&m);
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
memset(f,,sizeof f);
dfs(,);
printf("%d\n",(f[][][]+f[][][])%mod);
}
return ;
}
G,又是wqs二分,我又没有想到,想斜率,斜率,斜率。二分减去的权值,然后做最小匹配并记录边数就好了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 40050
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
char B[<<],*S=B,*T=B;
#define getc (S==T&&(T=(S=B)+fread(B,1,1<<15,stdin),S==T)?0:*S++)
inline int read()
{
int x=;char ch=getc;
while(ch<''|ch>'')ch=getc;
while(ch>=''&ch<='')x=*x+(ch^),ch=getc;
return x;
}
int a[N][],b[N][],n,m,K,Tim;
int g[][],now,last,num;
LL f[][],Ans,ans;
void dp(int i,int j,LL v){
now^=;last^=;
memset(f[now],0x3f,sizeof f[now]);
f[now][]=g[now][]=;
for(register int k=,x,y,nk;k<(<<m+);++k)if(f[last][k]<inf){
x=(k>>j-)&,y=(k>>j)&;
LL nf=f[last][k],ng=g[last][k];
if(x&&y)continue;
if(x){
nk=k^(<<j-);
if(nf<f[now][nk]||(nf==f[now][nk]&&ng>g[now][nk]))
f[now][nk]=nf,g[now][nk]=ng;
}
else if(y){
nk=k^(<<j);
if(nf<f[now][nk]||(nf==f[now][nk]&&ng>g[now][nk]))
f[now][nk]=nf,g[now][nk]=ng;
}
else if(!x&&!y){
if(i<n){
nk=k^(<<j-);
if(nf+a[i][j]-v<f[now][nk]||(nf+a[i][j]-v==f[now][nk]&&ng+>g[now][nk]))
f[now][nk]=nf+a[i][j]-v,g[now][nk]=ng+;
}
if(j<m){
nk=k^(<<j);
if(nf+b[i][j]-v<f[now][nk]||(nf+b[i][j]-v==f[now][nk]&&ng+>g[now][nk]))
f[now][nk]=nf+b[i][j]-v,g[now][nk]=ng+;
}
if(nf<f[now][k]||(nf==f[now][k]&&ng>g[now][k]))
f[now][k]=nf,g[now][k]=ng;
}
}
}
bool work(LL x){
now=;last=;
memset(f[now],0x3f,sizeof f[now]);
memset(g,,sizeof g);
f[now][]=g[now][]=;
for(int i=;i<=n;++i){
for(int j=;j<=m;++j)
dp(i,j,x);
for(int j=(<<m+)-;~j;--j){
if(j&)f[now][j]=inf,g[now][j]=;
else f[now][j]=f[now][j>>],g[now][j]=g[now][j>>];
}
}
ans=inf;
for(int i=;i<(<<m);i++)
if(f[now][i]<ans){ans=f[now][i],num=g[now][i];}
Ans=ans;
return num>=K;
}
int main(){
scanf("%d",&Tim);
while(Tim--){
n=read();m=read();K=read();
for(int i=;i<n;++i)
for(int j=;j<=m;++j)a[i][j]=read();
for(int i=;i<=n;++i)
for(int j=;j<m;++j)b[i][j]=read();
LL l=,r=1e14,mid,fin=;
while(l<=r){
mid=(*l+r)/;
if(work(mid))fin=mid,r=mid-;
else l=mid+;
}
work(fin);
printf("%lld\n",Ans+fin*K);
}
}
水平不行啊。
5.18
今天的话。前两个小时都在推T2的式子,后来推出来30分了,写上了,不过非常慢。又卡了卡常。之后看了看T1,感觉两个部分分都可以做,但是都没有写,看了看T3,不会。然后纠结去干T1还是T3,然而我树上莫队并不太会,链上的推的式子也很恶心,于是最后去想T3,然后发现是道水题,赶紧写了,因为方便,就先用int写了,没开long long,最后还剩2min的时候拍上了,赶紧把int都改成了long long结果inf没有改,就gg了。0+20+30=50。
T3,发现每个点一定和他第k+1个祖先颜色相同,于是把树缩上去之后dp就好了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define inf 0x3fffffffffffffff
#define N 2050
using namespace std;
int T,up,n,K,m;
int a[],b[],pp[];
LL f[N][],s[N][],ned[N][],g[];
namespace Read{
unsigned int SA, SB, SC;int p, A, B;
unsigned int rng61(){
SA ^= SA << ;
SA ^= SA >> ;
SA ^= SA << ;
unsigned int t = SA;
SA = SB;
SB = SC;
SC ^= t ^ SA;
return SC;
}
void gen(){
scanf("%d%d%d%d%u%u%u%d%d", &n, &K, &m, &p, &SA, &SB, &SC, &A, &B);
for(int i = ; i <= p; i++)scanf("%d%d", &a[i], &b[i]);
for(int i = p + ; i <= n; i++){
a[i] = rng61() % A + ;
b[i] = rng61() % B + ;
}
}
}
bool dfs(int x){
if((x<<)>up&&(x<(<<K))){
memset(f[x],,sizeof f[x]);
return ;
}
if((x<<)>up){
for(int i=;i<m;i++)f[x][i]=ned[x][i];
return ;
}
bool bo=;
if((x<<)<=up)bo|=dfs(x<<);
if((x<<|)<=up)bo|=dfs(x<<|);
for(int i=;i<m;i++)g[i]=;
if((x<<)<=up)for(int i=;i<m;i++)g[i]+=f[x<<][i];
if((x<<|)<=up)for(int i=;i<m;i++)g[i]+=f[x<<|][i];
if(x>=(<<K))bo=;
if(!bo){
memset(f[x],,sizeof f[x]);
return ;
}
for(int i=;i<m;i++){
f[x][i]=inf;
for(int j=;j<m;j++)
f[x][i]=min(f[x][i],g[j]+ned[x][(i-j+m)%m]);
}
return ;
}
int main(){
//freopen("test.in","r",stdin);
scanf("%d",&T);
while(T--){
Read::gen();
up=min(n,(<<K+)-);
memset(s,,sizeof s);
for(int i=;i<=up;i++)pp[i]=i,s[i][a[i]%m]+=b[i];
for(int i=(<<K+);i<=n;i++){
pp[i]=pp[i>>(K+)];
s[pp[i]][a[i]%m]+=b[i];
}
for(int i=;i<=up;i++){
for(int j=;j<m;j++){
ned[i][j]=;
for(int k=;k<m;k++)
ned[i][j]+=s[i][k]*((j-k+m)%m);
}
}
dfs();
printf("%lld\n",f[][]);
}
}
保持思考。
5.19
心情不好不想写了怎么办。。
5.20
还是写写吧。
首先是昨天的IOI赛制,看完T1,并不难想,但很不想写,看T2,割点裸题?yy了一下好像要用圆方树,但我还是不会,然后看T3,貌似不是很难推。然后先去看T3,写了几个式子都不对,有点难搞,这时候发现WQ把T2A了??!抉择了一下决定去写T2,写了1h写完了个假的圆方树,结果过不了第二个样例,然后yy了一下,搞了种类似缩点的打法,过了,交!A了。然后接着去看T3,感觉有规律,最后打出来了个表,要分解质因数,然后没时间了。0+100+0=100 rank2。
题解一会补。
T2,裸的圆方树+虚树。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 200500
#define pb push_back
using namespace std;
int e=,head[N];
struct edge{
int v,next;
}ed[N<<];
void add(int u,int v){
ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
vector <int> V[N];
int n,m,qr,T,K,ans,q[N],tim;
int dfn[N],low[N],top,sta[N],root,son,ge[N],tot;
void tarjan(int x){
dfn[x]=low[x]=++tim;
sta[++top]=x;
for(int i=;i<V[x].size();i++){
int v=V[x][i];
if(dfn[v]){
low[x]=min(low[x],dfn[v]);
continue;
}
tarjan(v);
low[x]=min(low[x],low[v]);
if(dfn[x]<=low[v]){
int y;tot++;
do{
y=sta[top--];
add(y,n+tot);
add(n+tot,y);
}while(y!=v);
add(x,n+tot);
add(n+tot,x);
if(x==root)son++;
else ge[x]=;
}
}
}
int fa[N],dep[N],val[N],s[N],seq[N<<],num,pos[N],mn[N<<][],lg[N<<];
void dfs(int x){
dep[x]=dep[fa[x]]+;
s[x]=s[fa[x]]+val[x];
seq[++num]=x;pos[x]=num;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x])continue;
fa[v]=x;dfs(v);
seq[++num]=x;
}
}
int Min(int x,int y){return dep[x]<dep[y]?x:y;}
void st_init(){
for(int i=,j=,cnt=;i<=num;i++){
if(i>(j<<))j<<=,cnt++;
lg[i]=cnt;
}
for(int i=;i<=num;i++)mn[i][]=seq[i];
for(int i=;i<=lg[num];i++)
for(int j=;j+(<<i)-<=num;j++)
mn[j][i]=Min(mn[j][i-],mn[j+(<<i-)][i-]);
}
int getlca(int x,int y){
x=pos[x];y=pos[y];
if(x>y)swap(x,y);
int k=lg[y-x+];
return Min(mn[x][k],mn[y-(<<k)+][k]);
}
bool cmp(int a,int b){
return pos[a]<pos[b];
}
int getans(int x,int y){
if(!y)return ;
return s[y]-s[x];
}
void init(){
for(int i=;i<=n;i++)V[i].clear();
e=;memset(head,,sizeof head);
top=tim=tot=num=;
memset(ge,,sizeof ge);
memset(fa,,sizeof fa);
memset(val,,sizeof val);
memset(dfn,,sizeof dfn);
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
init();
for(int i=,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
V[u].pb(v),V[v].pb(u);
}
root=;son=;
tarjan();
if(son>=)ge[]=;
for(int i=;i<=n;i++)if(ge[i])val[i]=;
dfs();
st_init();
scanf("%d",&qr);
while(qr--){
scanf("%d",&K);
ans=;
for(int i=;i<=K;i++){
scanf("%d",&q[i]);
if(ge[q[i]])ans--;
}
sort(q+,q+K+,cmp);
sta[]=q[];top=;
for(int i=;i<=K;i++){
int a=q[i],b=getlca(q[i],sta[top]),c=;
while(dep[sta[top]]>dep[b])
ans+=getans(sta[top],c),c=sta[top--];
ans+=getans(b,c);
if(sta[top]!=b)sta[++top]=b;
sta[++top]=a;
}
while(top>)ans+=getans(sta[top-],sta[top]),top--;
ans+=val[sta[]];
printf("%d\n",ans);
}
}
return ;
}
T3,首先我们设$f[i]$为最小循环节为i的方案数,可以发现$\sum_{d|m}{f[d]}=k^{ \lceil \frac{m}{2} \rceil }$,然后推一推式子就好了,其实挺麻烦的,不如打表,但是做法都是一样的,要分解质因数。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
int n,m,K,ans,mod,T;
int p[],cnt,mi[],pk[][];
int R(int x){
return rand()*rand()%x;
}
int mul(int a,int b,int p){
return (a*b-(int)(((long double)a*b+0.5)/p)*p+p)%p;
}
int qp(int a,int b,int c){
int ans=;
for(;b;b>>=,a=mul(a,a,c))
if(b&)ans=mul(ans,a,c);
return ans;
}
int prime[]={,,,,,};
bool miller_rabin(int x){
if(x==)return ;
for(int i=;i<=;i++)if(x==prime[i])return ;
for(int i=;i<=;i++)if(x%prime[i]==)return ;
int y=x-,cnt=;
while(!(y&))y>>=,cnt++;
for(int i=;i<=;i++){
int now=qp(prime[i],y,x),nxt;
for(int j=;j<=cnt;j++){
nxt=mul(now,now,x);
if(nxt==&&now!=&&now!=x-)return ;
now=nxt;
}
if(nxt!=)return ;
}
return ;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int pollard_rho(int x,int y){
int c=R(x-)+,d=c,g=;
for(int i=,j=;g==;i++){
c=(mul(c,c,x)+y)%x;
g=gcd(abs(c-d),x);
if(i==j)d=c,j<<=;
}
return g;
}
void divide(int x){
if(x==)return ;
if(miller_rabin(x)){
p[++cnt]=x;
return ;
}
int now=x;
while(now==x)now=pollard_rho(x,R(x-));
divide(now);
divide(x/now);
}
int getg(int x){
return qp(K,(x+)/,mod);
}
int geth(int x){
return (x&?x:(x>>))%mod;
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
void dfs(int x,int d,int now){
if(x==m+){
if((d&)&&(!((n/d)&)))return;
//printf("d==%lld n/d==%lld now==%lld g=%lld h=%lld\n",d,n/d,now,getg(d),geth(d));
UPD(ans,getg(d)*geth(d)%mod*(now%mod+mod)%mod);
return ;
}
for(int i=;i<=mi[x];i++)
dfs(x+,d*pk[x][i],(i==mi[x])?now:now*(-p[x]));
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&n,&K,&mod);K%=mod;
cnt=;
divide(n);
sort(p+,p+cnt+);
m=;
for(int i=,j=;i<=cnt;i=j){
p[++m]=p[i];mi[m]=;
pk[m][]=;
for(;j<=cnt&&p[j]==p[i];j++){
mi[m]++;
pk[m][mi[m]]=pk[m][mi[m]-]*p[i];
}
}
//for(int i=1;i<=m;i++)printf("%lld %lld\n",p[i],mi[i]);
ans=;
dfs(,,);
printf("%lld\n",ans);
}
return ;
}
正解的式子
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define int long long
using namespace std;
int n,m,K,ans,mod,T;
int p[],cnt,mi[],pk[][];
int R(int x){
return rand()*rand()%x;
}
int mul(int a,int b,int p){
return (a*b-(int)(((long double)a*b+0.5)/p)*p+p)%p;
}
int qp(int a,int b,int c){
int ans=;
for(;b;b>>=,a=mul(a,a,c))
if(b&)ans=mul(ans,a,c);
return ans;
}
int prime[]={,,,,,};
bool miller_rabin(int x){
if(x==)return ;
for(int i=;i<=;i++)if(x==prime[i])return ;
for(int i=;i<=;i++)if(x%prime[i]==)return ;
int y=x-,cnt=;
while(!(y&))y>>=,cnt++;
for(int i=;i<=;i++){
int now=qp(prime[i],y,x),nxt;
for(int j=;j<=cnt;j++){
nxt=mul(now,now,x);
if(nxt==&&now!=&&now!=x-)return ;
now=nxt;
}
if(nxt!=)return ;
}
return ;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int pollard_rho(int x,int y){
int c=R(x-)+,d=c,g=;
for(int i=,j=;g==;i++){
c=(mul(c,c,x)+y)%x;
g=gcd(abs(c-d),x);
if(i==j)d=c,j<<=;
}
return g;
}
void divide(int x){
if(x==)return ;
if(miller_rabin(x)){
p[++cnt]=x;
return ;
}
int now=x;
while(now==x)now=pollard_rho(x,R(x-));
divide(now);
divide(x/now);
}
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
void dfs(int x,int pos,int now){
if(x==m+){
UPD(ans,(now%mod+mod)%mod*qp(K,(pos+)/,mod)%mod);
return ;
}
if(p[x]!=)
for(int i=;i<=mi[x];i++)
dfs(x+,pos*pk[x][i],(i==mi[x])?now*pk[x][i]:now*(pk[x][i]*(-p[x])));
else
for(int i=;i<mi[x];i++)
dfs(x+,pos*pk[x][i+],(i==mi[x]-)?now*pk[x][i]:now*(pk[x][i]*(-p[x])));
}
signed main(){
//freopen("test.in","r",stdin);
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld",&n,&K,&mod);K%=mod;
cnt=;
divide(n);
sort(p+,p+cnt+);
m=;
for(int i=,j=;i<=cnt;i=j){
p[++m]=p[i];mi[m]=;
pk[m][]=;
for(;j<=cnt&&p[j]==p[i];j++){
mi[m]++;
pk[m][mi[m]]=pk[m][mi[m]-]*p[i];
}
}
ans=;
dfs(,,);
printf("%lld\n",ans);
}
return ;
}
打表的式子
今天更爆炸,上来玩了题答的前三个点,然后看了眼状态,wqA了T2,然后去搞,没搞出来,然后lc又在旁边大喊大叫说自己A了T1,之后写了个20分,打了个表,其实都要找出来了没仔细看,然后gg。T2puts("2")。0+25+58=83,T1幂处理的s的,T2暴力O(n^2)95?T3数组开小白扔10分?,什么玩意啊。。。操。。。菜!
T1式子懒得写了,就是一堆插板法合并起来。我的表都打出来了啊,就差一点,QAQ。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000500
using namespace std;
int n,m,s,invs,mx,mod,ans,fac[N<<],inv[N<<],pw[N<<];
void UPD(int &a,int b){
a=(a+b>=mod)?(a+b-mod):(a+b);
}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
if(m>n||m<)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
scanf("%d%d%d%d",&n,&m,&s,&mod);
mx=*m+n+s;
fac[]=;
for(int i=;i<=mx;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[mx]=qp(fac[mx],mod-);
for(int i=mx;i;i--)inv[i-]=1ll*inv[i]*i%mod;
invs=qp(s+,mod-);
pw[]=qp(s+,1ll*n*m%(mod-));
for(int i=;i<=(n+m)<<;i++)pw[i]=1ll*pw[i-]*invs%mod;
for(int i=;i<=m-;i++)
UPD(ans,1ll*C(n-+i,i)*C(n+m+s+i-,s-n+)%mod*pw[((i+n-)<<|)+(m-i-)]%mod);
for(int i=;i<=n-&&i<=s;i++)
UPD(ans,1ll*C(m-+i,i)*C(*m+n+s-,s-i)%mod*pw[((i+m-)<<|)+(n-i-)]%mod);
printf("%d\n",ans);
return ;
}
T2,最小链覆盖等于最长反链,具体我也不是特别懂。然后就是个裸的cdq啦。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 200500
using namespace std;
struct data{int id,x1,x2,x3,x4;}d[N];
bool cmpx1(data a,data b){return a.x1<b.x1;}
bool cmpx2(data a,data b){return a.x2<b.x2;}
int n,num[N],num_cnt,f[N],c[N],ans;
void add(int x,int y){for(;x<=num_cnt;x+=x&-x)c[x]=max(c[x],y);}
int query(int x){int y=;for(;x;x-=x&-x)y=max(y,c[x]);return y;}
void clear(int x){for(;x<=num_cnt;x+=x&-x)c[x]=;}
void cdq(int l,int r){
if(l==r){
f[d[l].id]=max(f[d[l].id],);
return ;
}
int mid=(l+r)>>;
cdq(l,mid);
sort(d+l,d+mid+,cmpx2);
sort(d+mid+,d+r+,cmpx1);
for(int i=mid+,j=l;i<=r;i++){
for(;j<=mid&&d[j].x2<d[i].x1;j++)
add(d[j].x4,f[d[j].id]);
f[d[i].id]=max(f[d[i].id],query(d[i].x3)+);
}
for(int i=l;i<=mid;i++)clear(d[i].x4);
cdq(mid+,r);
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
d[i].id=i;
scanf("%d%d%d%d",&d[i].x1,&d[i].x2,&d[i].x3,&d[i].x4);
num[++num_cnt]=d[i].x3;
num[++num_cnt]=d[i].x4;
}
sort(num+,num+num_cnt+);
for(int i=;i<=n;i++)
d[i].x3=lower_bound(num+,num+num_cnt+,d[i].x3)-num,
d[i].x4=lower_bound(num+,num+num_cnt+,d[i].x4)-num;
sort(d+,d+n+,cmpx1);
cdq(,n);
for(int i=;i<=n;i++)ans=max(ans,f[i]);
printf("%d\n",ans);
return ;
}
T3最小生成树有68分,正解好像是发现点构成了一些直线,然后巴拉巴拉什么的。
加油加油!!!!!!!
5.24IOI赛制
考试先看T1,不太会,然后想了一会,发现就是三维偏序,log2稳T,写了个cdq和暴力拍,没事就交了,60分,然后看后两题都只会暴力的样子,然后先写了T2的25分暴力,然后想40分,要处理所有回文串的出现次数?不行啊。然后看T3,尝试着puts2,10分,加了个puts0,40,又写了个快速幂,60。
T1,把三个两两组合做二维偏序,答案就是所有的减去这三个答案再除以2。证明的话,110+111+101+111+011+111=3*111+(110+101+011)=C(n,2)+2*111;
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 2000500
using namespace std;
int n,c[N];
void add(int x){for(;x<=n;x+=x&-x)c[x]++;}
int query(int x){int ans=;for(;x;x-=x&-x)ans+=c[x];return ans;}
LL ans;
struct data{int a,b,c;}d[N],tmp[N];
bool cmpa(data a,data b){return a.a<b.a;}
bool cmpb(data a,data b){return a.b<b.b;}
LL seed;
LL Rand(){return seed=((seed*)^)&((<<)-);}
LL work1(){
LL ans=;
memset(c,,sizeof c);
sort(d+,d+n+,cmpa);
for(int i=;i<=n;i++){
ans+=query(d[i].b);
add(d[i].b);
}
return ans;
}
LL work2(){
LL ans=;
memset(c,,sizeof c);
sort(d+,d+n+,cmpa);
for(int i=;i<=n;i++){
ans+=query(d[i].c);
add(d[i].c);
}
return ans;
}
LL work3(){
LL ans=;
memset(c,,sizeof c);
sort(d+,d+n+,cmpb);
for(int i=;i<=n;i++){
ans+=query(d[i].c);
add(d[i].c);
}
return ans;
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++)d[i].a=d[i].b=d[i].c=i;
scanf("%lld",&seed);for(int i=;i<=n;i++)swap(d[i].a,d[Rand()%i+].a);
scanf("%lld",&seed);for(int i=;i<=n;i++)swap(d[i].b,d[Rand()%i+].b);
scanf("%lld",&seed);for(int i=;i<=n;i++)swap(d[i].c,d[Rand()%i+].c);
ans=work1()+work2()+work3()-1ll*n*(n-)/;
printf("%lld\n",ans/);
return ;
}
T2,回文自动机+树剖,复习了回文自动机,不错。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define int long long
#define N 200005
using namespace std;
struct data{
char o[];
int x,l1,r1,l2,r2;
}d[N];
int n,m,a[N],s[N],L,nowl,nowr,pp1[N],pp2[N];
int e=,head[N];
struct edge{
int v,next;
}ed[N];
void add(int u,int v){
ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
int ch[N][],fail[N],len[N],tot;
int getfail(int x,int p){
while(s[p]!=s[p-len[x]-])x=fail[x];
return x;
}
int getfail1(int x,int p){
while(s[p]!=s[p+len[x]+])x=fail[x];
return x;
}
void extend(int pos,int c){
int p=getfail(pp1[pos-],pos);
if(!ch[p][c]){
int np=++tot;
len[np]=len[p]+;
int q=getfail(fail[p],pos);
fail[np]=ch[q][c];
ch[p][c]=np;
add(fail[np],np);
}
pp1[pos]=ch[p][c];
}
void travel(int x,int pos){
pp2[x]=pos;
if(x==)return ;
int p=getfail1(pos,x-);
travel(x-,ch[p][s[x-]]);
}
int dep[N],size[N],son[N],top[N],id[N],pp[N],num,fa[N][],LCA;
int sum[N<<],sum_val[N<<],lazy[N<<];
void dfs1(int x,int d){
dep[x]=d;size[x]=;
son[x]=tot+;
for(int i=;(<<i)<=dep[x];i++)
fa[x][i]=fa[fa[x][i-]][i-];
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
fa[v][]=x;dfs1(v,d+);
size[x]+=size[v];
if(size[v]>size[son[x]])son[x]=v;
}
}
void dfs2(int x,int t){
id[x]=++num,pp[num]=x,top[x]=t;
if(son[x]!=tot+)dfs2(son[x],t);
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==son[x])continue;
dfs2(v,v);
}
}
void build(int rt,int l,int r){
if(l==r){
sum[rt]=len[pp[l]]>?len[pp[l]]:;
return ;
}
int mid=(l+r)>>;
build(rt<<,l,mid);
build(rt<<|,mid+,r);
sum[rt]=sum[rt<<]+sum[rt<<|];
}
void update_lazy(int rt,int x){
lazy[rt]+=x;
sum_val[rt]+=x*sum[rt];
}
void pushdown(int rt){
if(lazy[rt]){
update_lazy(rt<<,lazy[rt]);
update_lazy(rt<<|,lazy[rt]);
lazy[rt]=;
}
}
void pushup(int rt){
sum_val[rt]=sum_val[rt<<]+sum_val[rt<<|];
}
void update(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y){
update_lazy(rt,);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y);
if(y>mid)update(rt<<|,mid+,r,x,y);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return sum_val[rt];
int mid=(l+r)>>;
pushdown(rt);
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return query(rt<<,l,mid,x,y)+query(rt<<|,mid+,r,x,y);
}
void update(int x){
do{
update(,,tot+,id[top[x]],id[x]);
x=fa[top[x]][];
}while(x<=tot);
}
void extendr(int pos){
int x=pp1[pos];
for(int i=;~i;i--)
if(pos-len[fa[x][i]]+<nowl)x=fa[x][i];
if(pos-len[x]+<nowl)x=fa[x][];
update(x);
}
void extendl(int pos){
int x=pp2[pos];
for(int i=;~i;i--)
if(pos+len[fa[x][i]]->nowr)x=fa[x][i];
if(pos+len[x]->nowr)x=fa[x][];
update(x);
}
int query(int x,int y){
int ans=;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans+=query(,,tot+,id[top[x]],id[x]);
x=fa[top[x]][];
}
if(dep[x]>dep[y])swap(x,y);
ans+=query(,,tot+,id[x],id[y]);
LCA=x;
return ans;
}
void queryr(int l1,int r1,int l2,int r2){
int x=pp2[l1],y=pp2[l2];
for(int i=;~i;i--)
if(l1+len[fa[x][i]]->r1)x=fa[x][i];
if(l1+len[x]->r1)x=fa[x][];
for(int i=;~i;i--)
if(l2+len[fa[y][i]]->r2)y=fa[y][i];
if(l2+len[y]->r2)y=fa[y][];
int ans=query(x,y);
if((len[LCA]<(r1-l1+)&&len[LCA]<(r2-l2+))&&s[l1+len[LCA]]==s[l2+len[LCA]])
ans-=query(,,tot+,id[LCA],id[LCA]);
printf("%lld\n",ans);
}
void queryl(int l1,int r1,int l2,int r2){
int x=pp1[r1],y=pp1[r2];
for(int i=;~i;i--)
if(r1-len[fa[x][i]]+<l1)x=fa[x][i];
if(r1-len[x]+<l1)x=fa[x][];
for(int i=;~i;i--)
if(r2-len[fa[y][i]]+<l2)y=fa[y][i];
if(r2-len[y]+<l2)y=fa[y][];
int ans=query(x,y);
if((len[LCA]<(r1-l1+)&&len[LCA]<(r2-l2+))&&s[r1-len[LCA]]==s[r2-len[LCA]])
ans-=query(,,tot+,id[LCA],id[LCA]);
printf("%lld\n",ans);
}
signed main(){
scanf("%lld%lld",&n,&m);
for(int i=;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=;i<=m;i++){
scanf("%s",d[i].o);
if(d[i].o[]=='a')
scanf("%lld",&d[i].x);
else scanf("%lld%lld%lld%lld",&d[i].l1,&d[i].r1,&d[i].l2,&d[i].r2);
}
s[]=-;
for(int i=m;i;i--)
if(d[i].o[]=='a'&&d[i].o[]=='l')
s[++L]=d[i].x;
nowl=L+;
for(int i=;i<=n;i++)s[++L]=a[i];
nowr=L;
for(int i=;i<=m;i++)
if(d[i].o[]=='a'&&d[i].o[]=='r')
s[++L]=d[i].x;
s[L+]=-;
tot=;len[]=-;
fail[]=fail[]=;
add(,);
for(int i=;i<=L;i++)
extend(i,s[i]);
travel(L+,);
fa[][]=tot+;
dfs1(,);
dfs2(,);
build(,,tot+);
for(int i=nowl;i<=nowr;i++)extendr(i);
for(int i=;i<=m;i++){
if(d[i].o[]=='t'){
if(d[i].o[]=='l')queryl(nowl-+d[i].l1,nowl-+d[i].r1,nowl-+d[i].l2,nowl-+d[i].r2);
else queryr(nowl-+d[i].l1,nowl-+d[i].r1,nowl-+d[i].l2,nowl-+d[i].r2);
}
else{
if(d[i].o[]=='l')extendl(--nowl);
else extendr(++nowr);
}
}
return ;
}
5.25
先看T1,推了推,只会60暴搜,然后看T2,不太清楚,看T3,好像会20骗分。写T1,写完60分总感觉可以直接dp,但是怎么想也想不出来。然后又乱搞了一会,也没想出来啥别的做法,后来只剩90min了,赶紧去写T2,O(n)的式子推了我好久,花了一个多小时写完了,然后测了组极限数据,6s??后来发现是因为我在结构体里开了个vector,然后sort就炸了。最后没改完,只得了20分,T3暴力都没交。
本来感觉这场考试应该能考得不错的,然后就是炸,不行啊!
T1就是个简单的小dp,为什么我就想不到?包括ctscD1T1,我想了1h+,可能是这阵做的dp太少了吧,必须要思考,尤其是这种没思路的,想想最基本的状态定义还有转移,也许真的不难。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 100500
using namespace std;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int fac[N],inv[N];
int num[],now[],nw[],wcnt,Ans,n,len;
char s[N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int C(int n,int m){
if(m==n||m==)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int f[][];
void work(){
for(int i=;i<=;i++){
for(int j=;j<=;j++)now[j]=num[j];
now[i]--;now[-i]--;
int ii=;memset(f[ii],,sizeof f[ii]);
for(int j=max(,-now[]);j<=wcnt;j++)
for(int k=max(,now[]+j-now[]);j+k<=wcnt;k++)
UPD(f[ii][j+k],1ll*C(wcnt,j)*C(wcnt-j,k)%mod);
for(int j=;j<=;j++){
ii^=;memset(f[ii],,sizeof f[ii]);
for(int k1=max(,max(,now[-j])-now[j]),k2=now[j]+k1-now[-j];k1+k2<=wcnt;k1++,k2++){
for(int l=;l+k1+k2<=wcnt;l++)
UPD(f[ii][l+k1+k2],1ll*f[ii^][l]*C(wcnt-l,k1)%mod*C(wcnt-l-k1,k2)%mod);
}
}
UPD(Ans,f[ii][wcnt]);
}
}
int main(){
scanf("%s",s+);
len=strlen(s+);n=len>>;
for(int i=;i<=len;i++){
if(s[i]=='?')wcnt++;
else num[s[i]-'']++;
}
fac[]=;
for(int i=;i<=wcnt;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[wcnt]=qp(fac[wcnt],mod-);
for(int i=wcnt;i>=;i--)inv[i-]=1ll*inv[i]*i%mod;
work();
printf("%d\n",Ans);
return ;
}
T2,我犯了好几个小错误,在这说一下吧,首先是在结构体里开vector,虽然只有一个数,但是常数一下打了若干倍,其次就是在函数中定义变量没有初始化,再有就是inf开的不够大,反正全是小毛病,细心,再细心。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 100500
#define eps 1e-12
#define inf 0x7fffffffffffffff
#define pb push_back
using namespace std;
int n;
double w[N],sw[N],d[N],sd[N],SUM[N],DIS[N],Ans[N],l[N],r[N],down,up;
int tot,top,be,pp[N];
vector <int> V[N];
struct point{
double x,y;
int id;
point(){x=y=;}
point(double a,double b){x=a;y=b;}
point operator - (point a){return point(x-a.x,y-a.y);}
double operator * (point a){return x*a.y-y*a.x;}
}p[N],q[N];
double getdis(point a){
return sqrt(a.x*a.x+a.y*a.y);
}
bool cmp(point a,point b){
double now=(a-p[])*(b-p[]);
if(now==)return getdis(a-p[])<getdis(b-p[]);
return now>;
}
int getf(double a){
if(a<-eps)return -;
if(a>eps)return ;
return ;
}
int vis[N],cnt;
namespace work1{
void Main(){
sw[n]=sw[n-]+down;
double x1=,x2=,sum1=,sum2=;
x1=d[];sum1=w[n]*x1;
SUM[]=d[n]*sum1;
for(int i=n-;i>=;i--){
x1+=d[i+];
sum1+=w[i]*x1;
SUM[]+=d[i]*sum1;
}
for(int i=;i<=n;i++){
x2+=d[i];
sum2+=x2*w[i];
}
x2+=d[];sum2+=x2*w[];
for(int i=;i<=n;i++){
SUM[i]=SUM[i-]-d[i]*sum1+d[i]*sum2;
sum1+=d[i]*sw[n];sum1-=sd[n]*w[i];
sum2+=sd[n]*w[i];sum2-=d[i+]*sw[n];
}
double ans=SUM[];
for(int i=;i<=n;i++)
ans=min(ans,SUM[i]);
for(int i=;i<=n;i++)
if(getf(ans-SUM[i])==)cnt++,vis[i]=;
for(int i=;i<=n;i++){
if(vis[i])printf("%0.3f\n",1.0/1.0/(1.0*cnt));
else puts("0.000");
}
}
}
int main(){
scanf("%d%lf%lf",&n,&down,&up);
for(int i=;i<n;i++){
scanf("%lf",&w[i]);
sw[i]=sw[i-]+w[i];
}
sw[n]=sw[n-];
for(int i=;i<=n;i++){
scanf("%lf",&d[i]);
sd[i]=sd[i-]+d[i];
}
if(down==up){
work1::Main();
return ;
}
for(int i=;i<n;i++)
DIS[i]=(sd[n]-sd[i])*(sd[i]);
double x1=,x2=,sum1=,sum2=;
x1=d[];sum1=w[n]*x1;
SUM[]=d[n]*sum1;
for(int i=n-;i>=;i--){
x1+=d[i+];
sum1+=w[i]*x1;
SUM[]+=d[i]*sum1;
}
for(int i=;i<=n;i++){
x2+=d[i];
sum2+=x2*w[i];
}
x2+=d[];sum2+=x2*w[];
for(int i=;i<=n;i++){
SUM[i]=SUM[i-]-d[i]*sum1+d[i]*sum2;
sum1+=d[i]*sw[n];sum1-=sd[n]*w[i];
sum2+=sd[n]*w[i];sum2-=d[i+]*sw[n];
}
for(int i=;i<=n;i++){
p[i]=point(DIS[i],SUM[i]);
p[i].id=i;
}
swap(p[],p[n]);
sort(p+,p+n+,cmp);
tot=;
for(int i=,j=;i<=n;i=j){
p[++tot]=p[i];
for(;j<=n&&getf(p[i].x-p[j].x)==&&getf(p[i].y-p[j].y)==;j++)
V[tot].pb(p[j].id);
}
q[]=p[];top=;pp[]=;
for(int i=;i<=tot;i++){
while(top>&&(p[i]-q[top])*(q[top]-q[top-])>=)top--;
q[++top]=p[i];
pp[top]=i;
}
tot=top;
for(int i=;i<=tot;i++)p[i]=q[i];
while(tot>&&(p[tot].y>p[tot-].y||p[tot].x<p[tot-].x))tot--;
l[]=inf;
for(int i=;i<tot;i++){
r[i]=(p[i].y-p[i+].y)/(p[i+].x-p[i].x);
l[i+]=r[i];
}
r[tot]=;
for(int i=;i<=tot;i++)
if(r[i]<up){be=i;break;}
for(int i=be;i<=tot;i++){
if(l[i]<down)break;
double now=(min(up,l[i])-max(down,r[i]))/(up-down)/(1.0*V[pp[i]].size());
for(int j=;j<V[pp[i]].size();j++){
Ans[V[pp[i]][j]]=now;
}
}
for(int i=;i<=n;i++)printf("%0.3f\n",Ans[i]);
return ;
}
T3,挺好的一个脑洞题,不想写了,怎么我越来越懒了呢?
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 13333
#define LL long long
#define pb push_back
using namespace std;
int n,m,top,tot,e,pos;
struct data{
int u,v;
data(){}
data(int a,int b){u=a;v=b;}
}d[N];
bool cmpd(const data & a,const data & b){return a.u<b.u;}
struct point{
int x,y,id,be;
point(){x=y=;}
point(int a,int b){x=a;y=b;}
point operator - (const point & a)const{return point(x-a.x,y-a.y);}
LL operator * (const point & a)const{return 1ll*x*a.y-1ll*y*a.x;}
}p[N],q[N];
LL getdis(const point & a){
return sqrt(1ll*a.x*a.x+1ll*a.y*a.y);
}
bool cmp(const point & a,const point & b){
LL now=(a-p[])*(b-p[]);
if(now==)return getdis(a-p[])<getdis(b-p[]);
return now>;
}
bool check(const point & a,const point & b,const point & c,const point & p){
LL x1=(b-a)*(p-a),x2=(c-b)*(p-b),x3=(a-c)*(p-c);
if(x1==||x2==||x3==)return ;
if(x1>){if(x2<||x3<)return ;return ;}
if(x1<){if(x2>||x3>)return ;return ;}
}
void graham(){
tot=n+m;
for(int i=;i<=tot;i++)if(p[i].x<p[].x||(p[i].x==p[].x&&p[i].y<p[].y))
swap(p[],p[i]);
sort(p+,p+tot+,cmp);
q[]=p[];top=;
for(int i=;i<=tot;i++){
while(top>&&(p[i]-q[top])*(q[top]-q[top-])>)top--;
q[++top]=p[i];
}
int posl=,posr=top-;
for(;posl<=top&&q[posl].be==q[posl-].be;posl++);posl--;
for(;posr>=&&q[posr].be==q[posr+].be;posr--);posr++;
if(posl+<posr){
int c=q[posl+].be;
for(int i=posl+;i<posr;i++)
if(q[i].be!=c){puts("GG!");exit();}
for(int i=;i<=posl;i++)q[top+i]=q[i];
for(int i=;i<=top;i++)q[i]=q[i+posl];
pos=posr-posl;
}
else if(posl+==posr){
pos=posr;
}
}
int num;
vector<int> V[N];
void work(const point & a,const point & b,const point & c,int now){
int cnt1=,cnt2=;
for(int i=;i<V[now].size();i++){
if(p[V[now][i]].be==a.be)cnt1++;
else cnt2++;
}
if(!cnt1&&!cnt2)return ;
if(!cnt1){
for(int i=;i<V[now].size();i++)
if(check(a,b,c,p[V[now][i]])&&p[V[now][i]].be==b.be)d[++e]=data(b.id,p[V[now][i]].id);
return ;
}
if(!cnt2){
for(int i=;i<V[now].size();i++)
if(check(a,b,c,p[V[now][i]])&&p[V[now][i]].be==a.be)d[++e]=data(a.id,p[V[now][i]].id);
return ;
}
for(int i=;i<V[now].size();i++)
if(check(a,b,c,p[V[now][i]])&&p[V[now][i]].be==a.be){
d[++e]=data(a.id,p[V[now][i]].id);
int nxt=++num;
for(int j=;j<V[now].size();j++)
if(check(b,a,p[V[now][i]],p[V[now][j]]))
V[nxt].pb(V[now][j]);
work(b,a,p[V[now][i]],nxt);
nxt=++num;
for(int j=;j<V[now].size();j++)
if(check(c,a,p[V[now][i]],p[V[now][j]]))
V[nxt].pb(V[now][j]);
work(c,a,p[V[now][i]],nxt);
nxt=++num;
for(int j=;j<V[now].size();j++)
if(check(p[V[now][i]],b,c,p[V[now][j]]))
V[nxt].pb(V[now][j]);
work(p[V[now][i]],b,c,nxt);
return ;
}
}
int main(){
scanf("%d%d",&n,&m);
if(!n||!m){
for(int i=;i<max(n,m);i++)printf("%d %d\n",i,m);
return ;
}
for(int i=;i<=n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
p[i].id=i;p[i].be=;
}
for(int i=;i<=m;i++){
scanf("%d%d",&p[n+i].x,&p[n+i].y);
p[n+i].id=n+i;p[n+i].be=;
}
graham();
if(q[top].be==q[].be){
for(int i=;i<=n;i++){
if(p[i].be!=q[].be){
for(int j=;j<top;j++){
d[++e]=data(q[j].id,q[j+].id);
int now=++num;
for(int k=;k<=tot;k++)
if(check(p[i],q[j],q[j+],p[k]))V[now].pb(k);
work(p[i],q[j],q[j+],now);
}
int now=++num;
for(int k=;k<=tot;k++)
if(check(p[i],q[],q[top],p[k]))V[now].pb(k);
work(p[i],q[],q[top],now);
break;
}
}
sort(d+,d+e+,cmpd);
for(int i=;i<n;i++)printf("%d %d\n",d[i].u,d[i].v);
for(int i=n;i<n+m-;i++)printf("%d %d\n",d[i].u-n,d[i].v-n);
return ;
}
for(int i=;i<pos-;i++){
d[++e]=data(q[i].id,q[i+].id);
int now=++num;
for(int j=;j<=tot;j++)
if(check(q[pos],q[i],q[i+],p[j]))V[now].pb(j);
work(q[pos],q[i],q[i+],now);
}
for(int i=pos;i<top;i++){
d[++e]=data(q[i].id,q[i+].id);
int now=++num;
for(int j=;j<=tot;j++)
if(check(q[],q[i],q[i+],p[j]))V[now].pb(j);
work(q[],q[i],q[i+],now);
}
sort(d+,d+e+,cmpd);
for(int i=;i<n;i++)printf("%d %d\n",d[i].u,d[i].v);
for(int i=n;i<n+m-;i++)printf("%d %d\n",d[i].u-n,d[i].v-n);
}
加油啊!!!!
5.26
先看T1,要O(nqlog),有60分?然后看T2,一点不会,然后看T3,想了一个ac自动机的玄学做法,写出来小样例都过了,自己造的极限数据跑的很快,然后去想T1,发现很水,写了个线段树,然后拍上了。最后去写了T2的暴力,感觉能上200?80+60+25=165 rank6,T1被卡常了,根本不用线段树,直接开个变量就可以,T2的暴力倒是多拿了不少分,T3完全看错题了,竟然有分,挺好。
这次也暴露出了我的不少问题,T1线段树完全是多余的,但是我想到之后没有深入去思考本质,于是就gg了。然后又是T3的读题问题,原来是有一段时间想错误的题意,这次是完全看错题意,以后读题要细心再细心。
T1,我们发现只需处理相邻元素就好,然后每对相邻元素可能需要动的就是最高的不相同位,然后记录一下每一位就好了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000500
#define B 35
using namespace std;
int n,m,a[N],s[N],hi[N],num1[B],num2[B],bit[B],Ans; int read(){
int a=;char ch=getchar();
while(ch<''||ch>'')ch=getchar();
while(ch>=''&&ch<=''){a=a*+(ch^);ch=getchar();}
return a;
}
void work(){
Ans=;
for(int i=;~i;i--){
if(num1[i]&&num2[i]){puts("-1");return;}
if(num1[i])Ans|=bit[i];
}
printf("%d\n",Ans);
}
int main(){
bit[]=;
for(int i=;i<=;i++)bit[i]=bit[i-]<<;
n=read();
for(int i=;i<=n;i++)a[i]=read();
memset(hi,-,sizeof hi);
for(int i=;i<n;i++){
s[i]=a[i]^a[i+];
for(int j=;~j;j--)
if(s[i]&bit[j]){hi[i]=j;break;}
if(hi[i]!=-){
if(a[i]&bit[hi[i]])num1[hi[i]]++;
else num2[hi[i]]++;
}
}
work();
m=read();
for(int i=,x,y;i<=m;i++){
x=read();y=read();
if(x>){
if(hi[x-]!=-){
if(a[x-]&(bit[hi[x-]]))num1[hi[x-]]--;
else num2[hi[x-]]--;
}
s[x-]=a[x-]^y;
hi[x-]=-;
for(int j=;~j;j--)
if(s[x-]&bit[j]){hi[x-]=j;break;}
if(hi[x-]!=-){
if(a[x-]&bit[hi[x-]])num1[hi[x-]]++;
else num2[hi[x-]]++;
}
}
if(x<n){
if(hi[x]!=-){
if(a[x]&(bit[hi[x]]))num1[hi[x]]--;
else num2[hi[x]]--;
}
s[x]=y^a[x+];
hi[x]=-;
for(int j=;~j;j--)
if(s[x]&bit[j]){hi[x]=j;break;}
if(hi[x]!=-){
if(y&(bit[hi[x]]))num1[hi[x]]++;
else num2[hi[x]]++;
}
}
a[x]=y;
work();
}
return ;
}
T2,我们先把最短路跑出来,然后只保留有用的边,然后对于一段连续的路线来说,可以用斜率优化,然后随便搞搞就行了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#include <map>
#define inf 0x7fffffff
#define N 1000500
#define LL long long
#define pb push_back
using namespace std;
int n,m,v[N],t[N],s[N];
int dis[N],vis[N];
int e=,head[N];
LL f[N];
struct edge{
int u,v,w,id,next;
}ed[N];
void add(int u,int v,int w,int id){
ed[e].u=u;ed[e].v=v;ed[e].w=w;ed[e].id=id;
ed[e].next=head[u];head[u]=e++;
}
queue<int> q;
void spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,,sizeof vis);
q.push();
dis[]=;vis[]=;
while(!q.empty()){
int x=q.front();q.pop();vis[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(dis[x]+ed[i].w<dis[v]){
dis[v]=dis[x]+ed[i].w;
if(!vis[v]){vis[v]=;q.push(v);}
}
}
}
}
LL getf(int u,int v){return -2ll*dis[u]*dis[v]+1ll*dis[u]*dis[u]+f[u];}
double getcross(int x,int y){return (double)(1ll*dis[x]*dis[x]+f[x]-1ll*dis[y]*dis[y]-f[y])/1.0/(2ll*dis[x]-2ll*dis[y]);}
int in[N],tot;
vector <int> qu[N];
map <int,int> pp[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=,x,y,cnt,las;i<=m;i++){
scanf("%d",&cnt);
scanf("%d",&las);
for(int j=;j<=cnt;j++){
scanf("%d%d",&y,&x);
add(las,x,y,i);las=x;
}
}
spfa();
int le=e;
e=;memset(head,,sizeof head);
for(int i=;i<le;i++)
if(dis[ed[i].u]+ed[i].w==dis[ed[i].v]){
add(ed[i].u,ed[i].v,ed[i].w,ed[i].id);
in[ed[i].v]++;
}
memset(f,-0x3f,sizeof f);
f[]=;q.push();
while(!q.empty()){
int x=q.front();q.pop();
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v,p=ed[i].id;
if(!pp[x].count(p))pp[x][p]=++tot;
int now=pp[x][p];
pp[v][p]=now;
while(qu[now].size()>&&getcross(qu[now][qu[now].size()-],qu[now][qu[now].size()-])<getcross(x,qu[now][qu[now].size()-]))
qu[now].pop_back();
qu[now].pb(x);
while(qu[now].size()>&&getf(qu[now][qu[now].size()-],v)>getf(qu[now][qu[now].size()-],v))
qu[now].pop_back();
f[v]=max(f[v],1ll*dis[v]*dis[v]+getf(qu[now][qu[now].size()-],v));
in[v]--;
if(!in[v])q.push(v);
}
}
printf("%d %lld\n",dis[n],f[n]);
return ;
}
T3,这题调了我好久啊,按根号分类讨论,小的我们暴力在trie树上走,大的我们线段树优化转移,这里一定要注意前后缀的区别。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define LL long long
#define ull unsigned int
#define N 300500
#define P 333331
using namespace std;
int ch1[N][],dep1[N],w1[N],tot1,root1;
int ch2[N][],dep2[N],w2[N],tot2,root2;
int len,n,m,nn,w[],ln[],tot;
char s[N],c[N];
LL f[N];
ull pw[N],h[][N];
ull geth(int x,int l,int r){return h[x][r]-h[x][l-]*pw[r-l+];}
void add(int v,char *c){
int l=strlen(c);
int now=root1;
for(int i=;i<l;i++){
int t=c[i]-'';
if(!ch1[now][t]){
ch1[now][t]=++tot1;
dep1[tot1]=dep1[now]+;
}
now=ch1[now][t];
w1[now]=min(w1[now],v);
}
now=root2;
for(int i=l-;~i;i--){
int t=c[i]-'';
if(!ch2[now][t]){
ch2[now][t]=++tot2;
dep2[tot2]=dep2[now]+;
}
now=ch2[now][t];
w2[now]=min(w2[now],v);
}
}
LL minn[N<<],lazy[N<<];
void update(int rt,LL v){
if(lazy[rt]==-||lazy[rt]>v)lazy[rt]=v;
minn[rt]=min(minn[rt],v);
}
void pushdown(int rt){
if(lazy[rt]!=-){
update(rt<<,lazy[rt]);
update(rt<<|,lazy[rt]);
lazy[rt]=-;
}
}
void update(int rt,int l,int r,int x,int y,LL z){
if(x<=l&&r<=y){
update(rt,z);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid) update(rt<<|,mid+,r,x,y,z);
minn[rt]=min(minn[rt<<],minn[rt<<|]);
}
LL query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return minn[rt];
pushdown(rt);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return min(query(rt<<,l,mid,x,y),query(rt<<|,mid+,r,x,y));
}
int pp[N];
bool cmp(const int &a,const int &b){return w[a]<w[b];}
int main(){
memset(w1,0x7f,sizeof w1);
memset(w2,0x7f,sizeof w2);
root1=tot1=root2=tot2=;
scanf("%d%d%d",&len,&n,&m);
pw[]=;
for(int i=;i<N;i++)pw[i]=pw[i-]*P;
nn=;
scanf("%s",s+);
for(int i=;i<=len;i++)h[][i]=h[][i-]*P+s[i]+;
for(int i=,x;i<=n;i++){
scanf("%d%s",&x,c);
if(strlen(c)<=nn)add(x,c);
else {
w[++tot]=x;
ln[tot]=strlen(c);
for(int i=;i<=ln[tot];i++)h[tot][i]=h[tot][i-]*P+c[i-]+;
}
}
for(int i=;i<=tot;i++)pp[i]=i;
sort(pp+,pp+tot+,cmp);
memset(lazy,-,sizeof lazy);
memset(minn,0x3f,sizeof minn);
memset(f,0x3f,sizeof f);
f[]=;
{
for(int j=;j<=tot;j++){
int l=,r=min(len,ln[j]),mid,fin=;
while(l<=r){
mid=(l+r)>>;
if(geth(,,mid)==geth(j,,mid))fin=mid,l=mid+;
else r=mid-;
}
if(fin)update(,,len,,fin,f[]+w[j]);
}
update(,,len,,,);
for(int j=,now=root1;j<=len;j++){
int t=s[j]-'';
if(!ch1[now][t])break;
now=ch1[now][t];
f[dep1[now]]=min(f[dep1[now]],f[]+w1[now]);
}
}
for(register int i=,j,jj,now,t,l,r,mid,fin;i<=len;++i){
f[i]=min(f[i],query(,,len,i,i));
now=root2;
for(j=i;j;--j){
t=s[j]-'';
if(!ch2[now][t])break;
now=ch2[now][t];
f[i]=min(f[i],f[i-dep2[now]]+w2[now]);
}
int pos=i+;
for(j=;j<=tot;++j){
jj=pp[j];
l=max(,i-ln[jj]+),r=pos-,mid,fin=pos;
while(l<=r){
mid=(l+r)>>;
if(geth(,mid,i)==geth(jj,ln[jj]-i+mid,ln[jj]))
fin=mid,r=mid-;
else l=mid+;
}
if(fin<pos)f[i]=min(f[i],query(,,len,fin-,pos-)+w[jj]);
pos=fin;
}
pos=i;
for(j=;j<=tot;++j){
jj=pp[j];
l=pos+,r=min(len,i+ln[jj]),mid,fin=pos;
while(l<=r){
mid=(l+r)>>;
if(geth(,i+,mid)==geth(jj,,mid-i))fin=mid,l=mid+;
else r=mid-;
}
if(fin>pos)update(,,len,pos+,fin,f[i]+w[jj]);
pos=fin;
}
update(,,len,i,i,f[i]);
now=root1;
for(j=i+;j<=len;++j){
t=s[j]-'';
if(!ch1[now][t])break;
now=ch1[now][t];
f[i+dep1[now]]=min(f[i+dep1[now]],f[i]+w1[now]);
}
}
if(f[len]==f[len+])puts("-1");
else printf("%lld\n",f[len]);
return ;
}
我要上清华!!!!!
5.28
联考IOI赛制,先看了一遍题,感觉T1可做,T2又是偏序的问题,T3多项式数学之类的吧。然后想T1,发现每次可以割掉两个点,然后照着这个思路打了,0分,我一直觉得Bob不可能割边,然后又写了个暴搜,0分。两边拍,拍出反例了,想了一下,发现Bob必须要把整棵树割成一个完美匹配,dfs一遍就好了,然后A了,然后看T2,感觉随便维护个什么就行,然后先把两题的暴力写了,然后歇了一会,发现只剩1h,赶紧去写,然后差一点,没调完。GG。
T1,就是那样,dfs就行了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 5005
using namespace std;
int e=,head[N];
struct edge{
int u,v,next;
}ed[N<<];
void add(int u,int v){
ed[e].u=u;ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
int n,m,size[N];
bool dfs(int x,int fa){
size[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
bool bo=dfs(v,x);
if(bo)size[x]+=size[v];
}
if(size[x]>){
puts("Alan");
exit();
}
if(size[x]==)return ;
return ;
}
int main(){
//freopen("test.in","r",stdin);
//freopen("2.out","w",stdout);
scanf("%d%d",&n,&m);
if(m==){
if(n==)puts("Alan");
else if(n==)puts("Bob");
else puts("Alan");
return ;
}
if(n&){puts("Alan");return ;}
if(m<(n>>)-){puts("Alan");return ;}
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
for(int i=;i<=n;i++)
if(!dfs(i,)){puts("Bob");return ;}
puts("Alan");
return ;
}
T2,我写的set,貌似线段树就可以。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <set>
#define N 500500
#define pr pair<int,int>
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define int long long
using namespace std;
int n,n1,n2,n3,t2,t3,sum,ans;
struct QAQ{
priority_queue<int> a,b;
void ins(int x){a.push(x);}
void del(int x){b.push(x);}
void mt(){while(!b.empty()&&a.top()==b.top())a.pop(),b.pop();}
int top(){mt();if(a.empty())return ;return a.top();}
}s2,s3;
set<pr> ss;
set<pr> :: iterator it,iit;
vector<pr> V[N],V2[N],V3[N];
void insert(pr a){
//printf("insert::( %d , %d )\n",a.fi,a.se);
it=ss.lower_bound(a);
if(it!=ss.end()&&it->se>=a.se)return ;
pr now;
int xx,yy;
if(it!=ss.begin())
for(it--;;it--,ss.erase(now)){
now=*it;
if(now.se>a.se)break;
if(it==ss.begin())xx=t2;
else {iit=it;iit--;xx=iit->fi;}
iit=it;iit++;
if(iit==ss.end())yy=t3;
else yy=iit->se;
sum+=(it->fi-xx)*(it->se-yy);
if(it==ss.begin()){
ss.erase(now);
break;
}
}
ss.insert(a);
it=ss.find(a);
if(it==ss.begin())xx=t2;
else {iit=it;iit--;xx=iit->fi;}
iit=it;iit++;
if(iit==ss.end())yy=t3;
else yy=iit->se;
sum-=(a.fi-xx)*(a.se-yy);
//printf("ins::sum===%d\n",sum);
}
void add(pr a){
if(a.fi>t2&&a.se>t3)insert(a);
else{
if(a.fi<=t2)V2[a.fi].pb(a);
if(a.se<=t3)V3[a.se].pb(a);
}
}
signed main(){
scanf("%lld%lld%lld%lld",&n,&n1,&n2,&n3);
for(int i=,a1,a2,a3;i<=n;i++){
scanf("%lld%lld%lld",&a1,&a2,&a3);
s2.ins(a2);s3.ins(a3);
V[a1].pb(mk(a2,a3));
}
t2=s2.top();
t3=s3.top();
sum=(n2-t2)*(n3-t3);
for(int a1=,a2,a3;a1<=n1;a1++){
//printf("a1====%d t2===%d t3===%d sum====%d\n",a1,t2,t3,sum);
ans+=sum;
for(int i=,j,k;i<V[a1].size();i++){
a2=V[a1][i].fi;a3=V[a1][i].se;
//printf("a2==%d a3==%d\n",a2,a3);
s2.del(a2);
if(s2.top()<t2){
if(ss.empty()){
sum+=(t2-s2.top())*(n3-t3);
}
else{
it=ss.begin();
sum+=(n3-it->se)*(t2-s2.top());
}
//printf("sum1===%d\n",sum);
for(j=t2,t2=s2.top();j>t2;j--)
for(k=;k<V2[j].size();k++)if(t3<V2[j][k].se)
insert(V2[j][k]);
}
s3.del(a3);
if(s3.top()<t3){
if(ss.empty()){
sum+=(n2-t2)*(t3-s3.top());
}
else{
it=ss.end();it--;
sum+=(n2-it->fi)*(t3-s3.top());
}
//printf("sum2===%d\n",sum);
for(j=t3,t3=s3.top();j>t3;j--)
for(k=;k<V3[j].size();k++)if(t2<V3[j][k].fi)
insert(V3[j][k]);
}
add(V[a1][i]);
}
}
printf("%lld\n",ans);
return ;
}
T3,把期望的式子拆开,然后是第二类斯特林数+多项式,发现长度是mod级别的,然后快速幂就好了。
#include <bits/stdc++.h>
#define mod 2003
using namespace std;
int n,K,L,F,invk,ans,s[mod][mod];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
struct pol{
int f[mod];
pol(){memset(f,,sizeof f);}
pol operator * (pol a){
pol b;
for(int i=;i<mod;i++)
for(int j=;i+j<mod;j++)
UPD(b.f[i+j],f[i]*a.f[j]%mod);
return b;
}
}P;
pol qp(pol a,int b){
pol c;c.f[]=;
for(;b;b>>=,a=a*a)if(b&)c=c*a;
return c;
}
int getinv(int x){return x==?:mod-mod/x*getinv(mod%x)%mod;}
int main(){
scanf("%d%d%d%d",&n,&K,&L,&F);
for(int i=,j;i<=F;i++)
for(s[i][]=!i,j=;j<=i;j++)
s[i][j]=(s[i-][j-]+j*s[i-][j])%mod;
for(int i=;i<=F;i++)P.f[i]=s[F][i];
P=qp(P,L);invk=getinv(K%mod);
for(int i=,k1=,k2=;i<=n%mod;k1=1ll*k1*(n-i)%mod,k2=k2*invk%mod,i++)
UPD(ans,k1*k2%mod*P.f[i]%mod);
printf("%d\n",ans);
return ;
}
来吧!
5.29
先看T1,貌似不是很难,然后就开始想,想了好久还是只想出来了$O(n \cdot 2^{2tot} \cdot 2tot)$,发现只有三十分,无奈,写了出来,和暴力拍,拍上了。然后看T2,一眼看上去不可做的样子,然后发现有一个很显然的$O(n^{2})$的dp,然后写了出来,发现可以多项式求逆优化,然后就O(nlogn)了,写了好久写完了,发现是对的,然后5e5要跑5s,卡了卡常,觉得90分差不多,然后去手玩了题答的前三个点,只玩出来了7分。30+90+7=127,rank6,呜哇。
T1,考虑状态定义为以i为根的子树中已实现的状态为j,然后转移时发现是一个或卷积,就可以fwt优化了,状态不一定要是绝对状态,相对状态可能更加优秀。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 1050
using namespace std;
int p[]={,,,,,,,,,,,,,,};
int f[N][],cnt[],s[N],n,tot,len,ans;
long long a[N],s1,s2;
int e=,head[N];
struct edge{
int v,next;
}ed[N<<];
void add(int u,int v){
ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
void dfs(int x,int fa){
for(int i=;i<len;i++)f[x][i]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
int ss=s[x]^s[v],tt=(len-)^ss;
for(int j=tt;j;j=(j-)&tt)
UPD(f[x][ss|j],1ll*f[x][ss|j]*f[v][ss|j]%mod);
UPD(f[x][ss],1ll*f[x][ss]*f[v][ss]%mod);
}
for(int i=;i<len;i++){
if(cnt[(len-)^i]&)UPD(ans,mod-f[x][i]);
else UPD(ans,f[x][i]);
}
}
int main(){
scanf("%d%lld%lld",&n,&s1,&s2);
s2/=s1;
for(int i=;i<;i++)if(s2%p[i]==)
p[tot++]=p[i];
len=(<<tot);
for(int i=;i<len;i++)cnt[i]=cnt[i>>]+(i&);
for(int i=;i<=n;i++){
scanf("%lld",&a[i]);
a[i]/=s1;
for(int j=;j<tot;j++)
if(a[i]%p[j]==)s[i]|=(<<j);
}
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(,);
printf("%d\n",ans);
return ;
}
T2,设$f[i]$表示i个点连通的方案数,$g[i]$表示i个点的答案。
我推的式子是
$f[i]=i!-\sum_{j=1}^{i-1}{f[j] \cdot (i-j)!}$,$f=fac-fac \cdot f$ ,所以 $f=\frac{fac}{fac+1}$,
$g[i]=\sum{j=1}^{i}{f[j] \cdot g[i-j]}$,$g=f \cdot g+1$ ,$g=\frac{1}{1-f}$,
然后是两次多项式求逆,一次乘法。这样的可以把$f[0]=-1$,然后就是$f=-f \cdot fac -1$,$ f=\frac{-1}{fac+1}$,可以少一次乘法,再卡卡常就可以过了。
#include <bits/stdc++.h>
#define N 1050000
#define mod 998244353
using namespace std;
int n,a[N],b[N],c[N],tmp[N],len,inv,rev[N],pw[N];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void init(int n){
for(len=;len<=(n<<);len<<=);
inv=qp(len,mod-);
register int i,now;
for(i=;i<len;++i){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
for(i=pw[]=,now=qp(,(mod-)/len);i<=len;++i)
pw[i]=1ll*pw[i-]*now%mod;
}
void ntt(int *a,int o){
register int i,j,k;long long t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);++j){
t=1ll*(o==?pw[len/k*j]:pw[len-len/k*j])*a[i+j+(k>>)];
a[i+j+(k>>)]=(a[i+j]-t%mod+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
}
void getni(int *a,int *b,int n){
if(n==){b[]=qp(a[],mod-);return ;}
getni(a,b,(n+)>>);init(n);
register int i;
for(i=;i<n;++i)tmp[i]=a[i];
for(i=n;i<len;++i)tmp[i]=;
ntt(tmp,);ntt(b,);
for(i=;i<len;++i)b[i]=1ll*b[i]*(-1ll*tmp[i]*b[i]%mod+mod)%mod;
ntt(b,-);
for(i=;i<n;++i)b[i]=1ll*b[i]*inv%mod;
for(i=n;i<len;++i)b[i]=;
}
int main(){
register int i;
scanf("%d",&n);
for(i=a[]=;i<=n;++i)a[i]=1ll*a[i-]*i%mod;
getni(a,b,n+);
for(i=b[]=;i<=n;++i)b[i]=mod-1ll*(mod-b[i])*i%mod;
getni(b,c,n+);
printf("%d\n",c[n]);
}
加油,加油!!!
5.30
先看T1,想到了一个显然的dp,然后没有去写,看T2,会40暴力,T3会50,然后写T1,写完发现写傻了,然后优化了一波,发现极限数据30s,算了一下,大概60~80差不多,然后去把后两题暴力写了,T3发现好像是原来做过的一道lct,没仔细想,想了一会T2的循环节和矩阵,然后也没想出来,60+20+50=130,T2炸int了,凉。
T1,暴搜即可AC
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000005
#define O 8
using namespace std;
int n,pos[],len[],tot,f[][],cnt[],ans;
char s[N];
void gmin(int &a,int b){a=(a>b)?b:a;}
void work(int x,int y,int p1,int p2,int step){
if(abs((len[x]-p1)-(len[y]-p2))+step>=ans)return;
while(s[pos[x]+p1]==s[pos[y]+p2]&&p1<len[x]&&p2<len[y])p1++,p2++;
if(p1==len[x]){ans=min(ans,step+len[y]-p2);return;}
if(p2==len[y]){ans=min(ans,step+len[x]-p1);return;}
work(x,y,p1+,p2+,step+);
work(x,y,p1+,p2,step+);
work(x,y,p1,p2+,step+);
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%s",s+tot);
pos[i]=tot;
len[i]=strlen(s+tot);
tot+=len[i];
}
for(int i=;i<n;i++)
for(int j=i+;j<=n;j++){
ans=;
work(i,j,,,);
cnt[ans]++;
}
for(int i=;i<=;i++)printf("%d ",cnt[i]);puts("");
return ;
}
T2,特征多项式推通项公式,然后拆成两个分治fft就好了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 133333
#define mod 99991
#define inv4 24998
using namespace std;
const double pi=acos(-1.0);
struct cmx{
double x,y;
cmx(){x=y=;}
cmx(double a,double b){x=a;y=b;}
cmx operator + (cmx a){return cmx(x+a.x,y+a.y);}
cmx operator - (cmx a){return cmx(x-a.x,y-a.y);}
cmx operator * (cmx a){return cmx(x*a.x-y*a.y,x*a.y+y*a.x);}
cmx operator / (double a){return cmx(x/a,y/a);}
}a[N],b[N],wn[N];
int len,rev[N],n,K,w[N],ans,rt,f0,f1,a1,a2,f[N];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
void init(int n){
for(len=;len<=n;len<<=);
for(int i=;i<len;i++){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
for(int i=;i<=len;i++)
wn[i]=cmx(cos(*pi/len*i),sin(*pi/len*i));
}
void fft(cmx *a,int o){
register int i,j,k;
register cmx t;
for(i=;i<len;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);j++){
t=a[i+j+(k>>)]*wn[o==?len/k*j:len-len/k*j];
a[i+j+(k>>)]=a[i+j]-t;
a[i+j]=a[i+j]+t;
}
}
}
}
void cdq(int l,int r){
if(l==r){f[l]=qp(rt,w[l]);return ;}
int mid=(l+r)>>;
cdq(l,mid);cdq(mid+,r);
init(r-l+);
for(int i=;i<len;i++)a[i]=b[i]=cmx(,);
for(int i=l;i<=mid;i++)a[i-l+]=cmx(f[i],);
for(int i=mid+;i<=r;i++)b[i-mid]=cmx(f[i],);
a[]=cmx(,);b[]=cmx(,);
fft(a,);fft(b,);
for(int i=;i<len;i++)a[i]=a[i]*b[i];
fft(a,-);
for(int i=l;i<=r;i++)
f[i]=llround(a[i-l+].x/len)%mod;
}
int main(){
scanf("%d%d",&n,&K);
for(int i=;i<=n;i++)scanf("%d",&w[i]);
scanf("%d%d",&f0,&f1);
a1=1ll*(*f0-f1+mod)*inv4%mod;
a2=1ll*(f0+f1)*inv4%mod;
rt=mod-;cdq(,n);
UPD(ans,1ll*a1*f[K]%mod);
rt=;cdq(,n);
UPD(ans,1ll*a2*f[K]%mod);
printf("%d\n",ans);
return ;
}
T3,留坑不填。
赢在终点?
5.31
看了一遍题,T1貌似52分很好拿,T2是thusc2016的题答,T3原来zzh讲过,没仔细听。先写T1的暴力,写52分写了快2h,然后去看T3,一点一点部分分拿到了53分,快没时间了,去把T2的前两个点打出来了,34俩点没打完,31分。
T1,m小的时候特征多项式,否则O(n/m)统计路径权值积的和。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000500
#define mod 65537
#define LL long long
using namespace std;
LL n,m;
int f[N],s[N],ans;
template <typename _t>
void UPD(_t &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
struct pol{
LL a[];
pol(){memset(a,,sizeof a);}
pol operator * (const pol & B)const{
pol C;
for(int i=;i<m;i++)
for(int j=;j<m;j++)
C.a[i+j]+=a[i]*B.a[j];
for(int i=;i<*m-;i++)C.a[i]%=mod;
for(int i=*m-;i>=m;i--){
for(int j=i-;j>=i-m;j--)
UPD(C.a[j],C.a[i]);
C.a[i]=;
}
return C;
}
}p;
pol qp(pol a,LL b){
pol c;
c.a[]=;
for(;b;b>>=,a=a*a)
if(b&)c=c*a;
return c;
}
int qp(int a,LL b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
namespace work1{
void Main(){
f[]=s[]=;
for(int i=;i<=m;i++){
f[i]=(s[i-]-(i>m?s[i-m-]:)+mod)%mod;
s[i]=(s[i-]+f[i])%mod;
}
p.a[]=;
p=qp(p,n);
for(int i=;i<m;i++)
UPD(ans,1ll*p.a[i]*f[i+]%mod);
printf("%d\n",ans);
}
}
int fac[mod],inv[mod];
int C(int n,int m){
if(m>n||m<)return ;
if(m==n||m==)return ;
return 1ll*fac[n]*inv[m]*inv[n-m]%mod;
}
int Lucas(LL n,LL m){
if(m>n||m<)return ;
if(m==n||m==)return ;
return 1ll*Lucas(n/mod,m/mod)*C(n%mod,m%mod)%mod;
}
int gets(LL n){
LL ans=;
int up=n/(m+);
int now=qp(,n),inv=qp(qp(,m+),mod-);
for(int i=;i<=up;i++,now=1ll*now*inv%mod){
if(i&)ans-=1ll*Lucas(n-i*m,i)*now;
else ans+=1ll*Lucas(n-i*m,i)*now;
}
return (ans%mod+mod)%mod;
}
int main(){
scanf("%lld%lld",&n,&m);
if(m==){puts("");return ;}
if(m<){work1::Main();return ;}
fac[]=;
for(int i=;i<mod;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[mod-]=qp(fac[mod-],mod-);
for(int i=mod-;i;i--)inv[i-]=1ll*inv[i]*i%mod;
ans=(gets(n)-(n>=m?gets(n-m):)+mod)%mod;
printf("%d\n",ans);
return ;
}
T3,当A大的时候,我们对于每个权值分别dp就好了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1005
#define mod 998244353
using namespace std;
int n,m,A,Ans,T,lim[N],f[N][N],num[N],num_cnt,ww[N],ww_cnt,q[N],top,L[N];
struct data{int l,r,w;}d[N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int solve(int x){
top=;
for(int i=;i<num_cnt;i++)
if(lim[i]==ww[x]){q[++top]=i;L[top]=;memset(f[top],,sizeof f[top]);}
if(!top)return ;
for(int i=;i<=m;i++){
if(d[i].w!=ww[x])continue;
int l=lower_bound(q+,q+top+,d[i].l)-q;
int r=lower_bound(q+,q+top+,d[i].r)-q-;
if(l>r)return ;
L[r]=max(L[r],l);
}
f[][]=;
for(int i=;i<=top;i++){
int v1=qp(ww[x],num[q[i]+]-num[q[i]]),v2=qp(ww[x]-,num[q[i]+]-num[q[i]]);
for(int j=i-;~j;j--){
if(j>=L[i])UPD(f[i][j],1ll*v2*f[i-][j]%mod);
UPD(f[i][i],1ll*f[i-][j]*(v1-v2+mod)%mod);
}
}
int ans=;
for(int i=;i<=top;i++)UPD(ans,f[top][i]);
return ans;
}
void work(){
scanf("%d%d%d",&n,&m,&A);
for(int i=;i<=m;i++)
scanf("%d%d%d",&d[i].l,&d[i].r,&d[i].w);
num_cnt=ww_cnt=;
num[++num_cnt]=;num[++num_cnt]=n+;
for(int i=;i<=m;i++){
num[++num_cnt]=d[i].l;
num[++num_cnt]=d[i].r+;
ww[++ww_cnt]=d[i].w;
}
sort(num+,num+num_cnt+);
num_cnt=unique(num+,num+num_cnt+)-num-;
sort(ww+,ww+ww_cnt+);
ww_cnt=unique(ww+,ww+ww_cnt+)-ww-;
for(int i=;i<=num_cnt;i++)lim[i]=A+;
for(int i=;i<=m;i++){
d[i].l=lower_bound(num+,num+num_cnt+,d[i].l)-num;
d[i].r=lower_bound(num+,num+num_cnt+,d[i].r+)-num;
for(int j=d[i].l;j<d[i].r;j++)
lim[j]=min(lim[j],d[i].w);
}
Ans=;
for(int i=;i<=ww_cnt;i++){
Ans=1ll*Ans*solve(i)%mod;
if(!Ans){puts("");return ;}
}
for(int i=;i<num_cnt;i++)
if(lim[i]==A+)Ans=1ll*Ans*qp(A,num[i+]-num[i])%mod;
printf("%d\n",Ans);
}
int main(){
scanf("%d",&T);
while(T--)work();
return ;
}
冷静,稳住。
6.1,先祝自己儿童节快乐!
去pku的已经走了,四个人还要考试。。。
先看题,他们说做过T3,于是换成了个我做过的。。。先把T3写完了,一直RE,后来发现网络流多路增广写残了,调了好久。然后看T1,感觉还是个吊吊的dp,想了好久状态也不会定义,然后写了30分暴力,去看T2,感觉不是很难,就是有点乱,先写了40分暴力,然后yy了一会把链上的写出来了,然后就开始神游了。。30+60+100=190,估分准确,不错。T2只需要一个dfs记忆化搜索就行,T1正解爆搜。。。
T1,我们按照左端点dp,发现对后面影响不同的只有两种情况,然后搜就行了,交点用树状数组来维护。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 55
#define inf 0x7fffffff
using namespace std;
int pw[N],vis[N],pp[N],a[N],l[N],r[N],id[N],tot,num,T,n,ans;
struct bit{
int c[N];
void update(int x,int y){for(;x<=*num;x+=x&-x)c[x]+=y;}
int query(int x){int y=;for(;x;x-=x&-x)y+=c[x];return y;}
int query(int l,int r){return query(r)-query(l-);}
}up,down;
void dfs(int x,int step){
if(step>=ans)return ;
if(x==num+){ans=step;return;}
int v1,v2;
v1=up.query(l[x],r[x]),v2=up.query(r[x],num*)+down.query(l[x],num*);
up.update(r[x],);
dfs(x+,step+min(v1,v2));
up.update(r[x],-);
v1=down.query(l[x],r[x]),v2=down.query(r[x],num*)+up.query(l[x],num*);
down.update(r[x],);
dfs(x+,step+min(v1,v2));
down.update(r[x],-);
}
int main(){
scanf("%d",&T);
pw[]=;
for(int i=;i<=;i++)pw[i]=pw[i-]*;
while(T--){
scanf("%d",&n);
memset(vis,,sizeof vis);
memset(pp,,sizeof pp);
for(int i=,x;i<=n;i++){
scanf("%d",&x);
if(!vis[x])vis[x]=i;
else pp[vis[x]]=i,pp[i]=vis[x];
}
tot=;
for(int i=;i<=n;i++)
if(pp[i])a[++tot]=i,id[i]=tot;
num=;
for(int i=;i<=tot;i++)
if(pp[a[i]]>a[i])
l[++num]=i,r[num]=id[pp[a[i]]];
ans=inf;
dfs(,);
printf("%d\n",ans);
}
return ;
}
T2,dp数组f[i][j]表示i点以j边为父亲边时的最大可能的首位数,然后输出时乱搞一下就行了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000500
#define inf 0x3f3f3f3f
using namespace std;
char B[<<],*SS=B,*TT=B;
#define getc (SS==TT&&(TT=(SS=B)+fread(B,1,1<<15,stdin),SS==TT)?0:*SS++)
int read(){
int a=;char ch=getc;
while(ch<''||ch>'')ch=getc;
while(ch>=''&ch<=''){a=a*+(ch^);ch=getc;}
return a;
}
int n,in[N],a[N][],f[N][];
int find(int x,int v){return a[x][]==v?:(a[x][]==v?:);}
int dfs(int x,int o){
if(f[x][o]!=inf)return f[x][o];
if(in[x]<)f[x][o]=x;
for(int i=;i<=in[x];i++)if(i!=o)
f[x][o]=min(f[x][o],dfs(a[x][i],find(a[x][i],x)));
return f[x][o];
}
void print(int x,int fa,int o){
if(in[x]==){printf("%d ",x);return;}
if(!o){
printf("%d ",x);
if(in[x]==){
int v=a[x][]==fa?a[x][]:a[x][];
if(v<=f[v][find(v,x)])print(v,x,);
else print(v,x,);
}
else{
int v1=,v2=;
for(int i=;i<=in[x];i++)
if(a[x][i]!=fa)v1==?v1=a[x][i]:v2=a[x][i];
if(f[v1][find(v1,x)]>f[v2][find(v2,x)])swap(v1,v2);
print(v1,x,);print(v2,x,);
}
}
else{
if(in[x]==){
int v=a[x][]==fa?a[x][]:a[x][];
if(x==f[x][find(x,fa)]){printf("%d ",x);print(v,x,);}
else{print(v,x,);printf("%d ",x);}
}
else{
int v1=,v2=;
for(int i=;i<=in[x];i++)
if(a[x][i]!=fa)v1==?v1=a[x][i]:v2=a[x][i];
if(f[v1][find(v1,x)]>f[v2][find(v2,x)])swap(v1,v2);
print(v1,x,);printf("%d ",x);print(v2,x,);
}
}
}
int main(){
n=read();
for(int i=;i<=n;i++){
in[i]=read();
for(int j=;j<=in[i];j++)
a[i][j]=read();
}
memset(f,0x3f,sizeof f);
for(int i=;i<=n;i++)
for(int j=;j<=in[i];j++)
dfs(i,j);
int rt=;
for(int i=;i<=n;i++)
if(in[i]<){rt=i;break;}
in[rt]++;
print(rt,,);
puts("");
return ;
}
T3,傻逼网络流。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
using namespace std;
#define inf 0x7fffffff
#define N 15005
#define id(x,y) (7*(x)-(7-(y)))
int id[][],tot,n,m,S,T;
int dis[N],vis[N],Cost,Flow,allflow;
int e=,head[N];
struct edge{
int u,v,f,w,next;
}ed[N<<];
void add(int u,int v,int f,int w){
ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;ed[e].w=-w;
ed[e].next=head[v];head[v]=e++;
}
bool spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,,sizeof vis);
queue<int> q;
q.push(S);dis[S]=;
while(!q.empty()){
int x=q.front();q.pop();vis[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]>dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;
if(!vis[v]){vis[v]=;q.push(v);}
}
}
}
return dis[T]!=dis[];
}
int dfs(int x,int f){
vis[x]=;
if(x==T){
Flow+=f;
Cost+=f*dis[T];
return f;
}
int ans=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]==dis[x]+ed[i].w&&!vis[v]){
int nxt=dfs(v,min(f,ed[i].f));
ans+=nxt;f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
}
if(!f)break;
}
return ans;
}
void work(){
while(spfa()){
do{
memset(vis,,sizeof vis);
dfs(S,inf);
}while(vis[T]==);
}
if(Flow==allflow)printf("%d\n",Cost/);
else puts("-1");
}
void work1(int pos,int val){
int cnt=,a[];
for(int i=;i<;i++){
a[i]=(val>>i)&;
cnt+=a[i];
}
allflow+=cnt;
add(S,id(pos,),cnt,);
if(cnt==){
if(a[]==||a[]==){
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:);
add(id(pos,),id(pos,),,a[]==?:);
}
else{
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:);
add(id(pos,),id(pos,),,a[]==?:);
}
}
if(cnt==){
if(val==)add(id(pos,),id(pos,),,),add(id(pos,),id(pos,),,);
else if(val==)add(id(pos,),id(pos,),,),add(id(pos,),id(pos,),,);
else {
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
for(int i=;i<;i++){
if(a[i])add(id(pos,+(i&)),id(pos,+i),,);
else add(id(pos,+(i&)),id(pos,+i),,);
}
}
}
if(cnt==){
if(a[]==||a[]==){
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:-);
add(id(pos,),id(pos,),,a[]==?:-);
}
else{
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:-);
add(id(pos,),id(pos,),,a[]==?:-);
}
}
if(cnt==){
for(int i=;i<;i++)
add(id(pos,),id(pos,+i),,);
}
}
void work2(int pos,int val){
int cnt=,a[];
for(int i=;i<;i++){
a[i]=(val>>i)&;
cnt+=a[i];
}
add(id(pos,),T,cnt,);
if(cnt==){
if(a[]==||a[]==){
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:);
add(id(pos,),id(pos,),,a[]==?:);
}
else{
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:);
add(id(pos,),id(pos,),,a[]==?:);
}
}
if(cnt==){
if(val==)add(id(pos,),id(pos,),,),add(id(pos,),id(pos,),,);
else if(val==)add(id(pos,),id(pos,),,),add(id(pos,),id(pos,),,);
else {
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
for(int i=;i<;i++){
if(a[i])add(id(pos,+i),id(pos,+(i&)),,);
else add(id(pos,+i),id(pos,+(i&)),,);
}
}
}
if(cnt==){
if(a[]==||a[]==){
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:-);
add(id(pos,),id(pos,),,a[]==?:-);
}
else{
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,);
add(id(pos,),id(pos,),,a[]==?:-);
add(id(pos,),id(pos,),,a[]==?:-);
}
}
if(cnt==){
for(int i=;i<;i++)
add(id(pos,+i),id(pos,),,);
}
}
int main(){
scanf("%d%d",&n,&m);
S=*n*m+;T=S+;
for(int i=,x;i<=n;i++){
for(int j=;j<=m;j++){
scanf("%d",&x);
id[i][j]=++tot;
if(!((i+j)&))work1(id[i][j],x);
else work2(id[i][j],x);
}
}
for(int i=;i<=n;i++){
for(int j=;j<m;j++)
if(!((i+j)&))add(id(id[i][j],),id(id[i][j+],),,);
else add(id(id[i][j+],),id(id[i][j],),,);
}
for(int i=;i<n;i++){
for(int j=;j<=m;j++)
if(!((i+j)&))add(id(id[i][j],),id(id[i+][j],),,);
else add(id(id[i+][j],),id(id[i][j],),,);
}
work();
return ;
}
明天,后天,大后天,大大后天。。。
凉凉
前一百降六十,为什么不能进队呢?
刷了刷去年NOI的题,除了Day2T3都写了,写一下总结。
Day1T1,其实就是区间覆盖线段树,然后查单点。要看进位借位的性质,就是找前面第一个0/1的位置,这都可以用线段树来实现。
Day1T2,复杂度分析的题,其实并不难,只要敢打暴力就行,但是具体分析也并不难。
Day1T3,挺好的dp,首先是把等于K转化成小于等于K,然后这样dp我们需要下面一行安全,别的未知的方案数,于是我们dp一个g数组,通过h数组转移,然后还考察了特征多项式的知识,非常好的一道题。
Day2T1,2-sat+状压枚举方案,原来输出方案直接输出两个点里联通块编号小的就行。
Day2T2,也是不错的一道题,我们直接考虑贪心放权值最大的,然后从最后一天往前放,然后处理一下就好了。
Day2T3,平衡树维护动态凸包?
东北育才集训,去年,今年。。
Day1。
先看了题,T1我猜是结论题,T2看起来每个点维护个儿子的信息就行了,T3做过,只会30。然后开始想T1,发现我不会,感觉可能是容斥组合数什么之类的,但是不会,然后看T2,发现我要支持区间+1,维护异或和,发现我也不会,T3又想了一会,提示里的式子我还是看不懂,然后去刚T2,最后写的暴力,然后去看T1,把k=3打表打出了O(1)的,然后忘了模了!!,操。。。凉凉。
T1,分情况讨论,容斥,组合数,把每段的长度看成变量,枚举一个H,然后化化式子就好了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 1000000
#define mod 1000109107
using namespace std;
int T,n,m,nn,K,fac[N+],inv[N+];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
LL C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
LL calc3(){return 1ll*(C(n,)-1ll*(nn-)*(nn-)/*n%mod+mod)%mod;}
LL calc2(){return 1ll*n*(C(nn,m-)+C(n-nn,m-))%mod;}
LL calc1(){return 1ll*n*((nn-)*C(n-nn,m-)%mod+C(n-nn,m-))%mod;}
int main(){
fac[]=;
for(int i=;i<=;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[]=qp(fac[],mod-);
for(int i=;i;i--)inv[i-]=1ll*inv[i]*i%mod;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&K);nn=(n+)>>;
if(K>)puts("");
else if(K==)printf("%lld\n",m==?calc3():0ll);
else if(K==)printf("%lld\n",m==?1ll*(nn-)*(nn-)/*n%mod:calc2());
else if(K==)printf("%lld\n",m==?0ll:(calc1()+*(mod-calc2()))%mod);
else if(K==)printf("%lld\n",m==?0ll:(C(n,m)-calc1()+calc2()+mod)%mod);
}
return ;
}
T2,对于每个点,维护其所有儿子的一个反着的01Trie,然后就可以很轻松的支持我上面说的那两个操作了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 500500
#define mod 1000109107
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int e=,head[N];
struct edge{int v,next;}ed[N<<];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
int n,m,Ans,cnt[N][][],tim[N],val[N],fa[N];
int root[N],tot,size[N<<],ch[N<<][];
void insert(int a,int val,int num){
if(!root[a])root[a]=++tot;
size[root[a]]+=num;
for(int i=,x=root[a];i<;i++){
int t=(val>>i)&;
cnt[a][i][t]+=num;
if(!ch[x][t])ch[x][t]=++tot;
x=ch[x][t];
size[x]+=num;
}
}
void erase(int a,int val,int num){
size[root[a]]-=num;
for(int i=,x=root[a];i<;i++){
int t=(val>>i)&;
cnt[a][i][t]-=num;
if(!ch[x][t])ch[x][t]=++tot;
x=ch[x][t];
size[x]-=num;
}
}
void change(int a){
for(int i=,x=root[a];i<;i++){
cnt[a][i][]+=size[ch[x][]]-size[ch[x][]];
cnt[a][i][]+=size[ch[x][]]-size[ch[x][]];
swap(ch[x][],ch[x][]);
if(!ch[x][])return ;
x=ch[x][];
}
}
int query(int x){
int ans=;
for(int i=;i<;i++)
if(cnt[x][i][]&)ans^=(<<i);
return ans;
}
void dfs(int x){
int num=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x])continue;
fa[v]=x;dfs(v);
num++;
}
if(num)insert(x,,num);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs();
for(int i=,x,now,gf,ans;i<=m;i++){
scanf("%d",&x);
gf=fa[fa[x]];now=val[fa[x]]+tim[gf];
change(x);ans=query(x);
if(fa[x])ans^=(now+);
UPD(Ans,1ll*ans*i*(i+)%mod);
if(gf){erase(gf,now,),insert(gf,now+,);}
tim[x]++;val[fa[x]]++;
}
printf("%d\n",Ans);
return ;
}
T3,我现在明白了,但是懒得写了。。。详见wxh的pdf。
Day2,因为今天要出发,所以只粗略的看了看题,发现T1是水题,切了,后面两题题意都不太清楚,就没想。
T1,sb点分树。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100500
#define mod 998244353
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int size[N],maxn[N],vis[N],root,allsize,n,t,m,nn,d[N];
int pw[N],sum1[N],sum2[N],fa[N],Ans;
int a[N<<],tot,L[N],dep[N],lg[N<<],mn[N<<][];
int e=,head[N];
struct edge{int v,next;}ed[N<<];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;} void dfs(int x,int f){
a[++tot]=x;L[x]=tot;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==f)continue;
dep[v]=dep[x]+;
dfs(v,x);
a[++tot]=x;
}
}
int Min(int x,int y){return dep[x]<dep[y]?x:y;}
void st_init(){
for(int i=,j=,k=;i<=tot;i++){
if(i>(j<<))j<<=,k++;
lg[i]=k;
}
for(int i=;i<=tot;i++)mn[i][]=a[i];
for(int i=;i<=lg[tot];i++)
for(int j=;j+(<<i)-<=tot;j++)
mn[j][i]=Min(mn[j][i-],mn[j+(<<i-)][i-]);
}
int getlca(int x,int y){
x=L[x];y=L[y];
if(x>y)swap(x,y);
int k=lg[y-x+];
return Min(mn[x][k],mn[y-(<<k)+][k]);
}
int getdis(int x,int y){
return dep[x]+dep[y]-*dep[getlca(x,y)];
} void getroot(int x,int f){
size[x]=;maxn[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==f||vis[v])continue;
getroot(v,x);
size[x]+=size[v];
maxn[x]=max(maxn[x],size[v]);
}
maxn[x]=max(maxn[x],allsize-size[x]);
if(maxn[x]<maxn[root])root=x;
}
void build(int x){
vis[x]=;
int now=allsize;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(vis[v])continue;
allsize=size[v]>size[x]?now-size[x]:size[v];
root=;getroot(v,);
fa[root]=x;build(root);
}
}
void update(int x){
UPD(Ans,sum1[x]);UPD(sum1[x],);
for(int i=x;fa[i];i=fa[i]){
UPD(Ans,1ll*(sum1[fa[i]]-sum2[i]+mod)*pw[getdis(x,fa[i])]%mod);
UPD(sum2[i],pw[getdis(x,fa[i])]);
UPD(sum1[fa[i]],pw[getdis(x,fa[i])]);
}
} int main(){
scanf("%d%d",&n,&t);
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
nn=n;
scanf("%d",&m);
for(int i=,x;i<=m;i++){
scanf("%d",&d[i]);
if(!d[i]){
scanf("%d",&x);n++;
add(x,n);add(n,x);
}
} pw[]=;
for(int i=;i<=n;i++)pw[i]=1ll*pw[i-]*%mod; dfs(,);st_init();
maxn[]=n+;root=;allsize=n;
getroot(,);build(root); for(int i=;i<=nn;i++)update(i);
for(int i=;i<=m;i++){
if(!d[i])update(++nn);
else printf("%d\n",Ans);
}
return ;
}
T2,普及组水题。
T3,发现每个点一定比他前面第一个f[j]=f[i]-1的j要大,还要比前面所有的f[j]=f[i]的大,于是直接连边维护LCT就行了,因为f相同的不超过20个,所以可以直接维护和。
NOI倒计时:1month4days
Day3
先看T1,感觉很可做,推了推,只会O(n^3),然后看T2,又是LCT?然后发现T3noip前做过,开始刚,想了1h多想出来了,码码码,过了样例和手造的小数据,觉得没啥问题,然后还是不会T1,写暴力写了好久,最后T2交的也是暴力,呜呜呜。
T1,丝薄dp题,我都不会,什么啊。。直接f[i][j]i表示i行j列的答案,枚举最后一行放的状态,然后O(1)转移即可。
include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 3050
using namespace std;
int n,m,ans,C2[N],pw[N],f[N][N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int main(){
scanf("%d%d",&n,&m);
if(n>m)swap(n,m);
for(int i=;i<=m;i++)C2[i]=1ll*i*(i-)/%mod;
for(int i=;i<=m;i++)f[][i]=f[i][]=;
for(int i=;i<=n;i++){
for(int j=i;j<=m;j++){
f[i][j]=f[i-][j];
UPD(f[i][j],4ll*j*f[i-][j-]%mod);
if(j>=)UPD(f[i][j],1ll*C2[j]*f[i-][j-]%mod);
if(i>=)UPD(f[i][j],1ll*j*(i-)*f[i-][j-]%mod);
f[j][i]=f[i][j];
}
}
printf("%d\n",(f[n][m]+mod-)%mod);
return ;
}
T2,就是LCT,考过的啊,菜菜菜。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 100500
using namespace std;
int n,val[N],num[N],num_cnt,q[N],top;
LL c[N];
void update(int x,int y){for(;x<=num_cnt;x+=x&-x)c[x]+=y;}
int query(int x){int y=;for(;x;x-=x&-x)y+=c[x];return y;}
void clear(int x){for(;x<=num_cnt;x+=x&-x){if(!c[x])return;c[x]=;}}
struct Node{
Node *fa,*ch[];
int id,size,val,lazy;
Node();
void change(int x){val=lazy=x;}
void pushup(){size=ch[]->size+ch[]->size+;}
void pushdown(){
if(lazy){
ch[]->change(lazy);
ch[]->change(lazy);
lazy=;
}
}
}*null=new Node(),tree[N];
Node :: Node(){
fa=ch[]=ch[]=null;
id=size=val=lazy=;
}
void init(){
for(int i=;i<=n;i++){
tree[i].fa=tree[i].ch[]=tree[i].ch[]=null;
tree[i].id=i;tree[i].size=;
tree[i].val=val[i];tree[i].lazy=;
}
}
int isroot(Node *x){return x->fa->ch[]!=x&&x->fa->ch[]!=x;}
int get(Node *x){return x->fa->ch[]==x;}
void pushdown(Node *x){
if(!isroot(x))pushdown(x->fa);
x->pushdown();
}
void rotate(Node *x){
Node *y=x->fa,*z=y->fa;
int w=get(x);
x->ch[w^]->fa=y;y->ch[w]=x->ch[w^];
y->fa=x;x->ch[w^]=y;
if(z->ch[]==y)z->ch[]=x;
if(z->ch[]==y)z->ch[]=x;
x->fa=z;
y->pushup();x->pushup();
}
void splay(Node *x){
pushdown(x);
Node *y;
for(;!isroot(x);rotate(x)){
y=x->fa;
if(!isroot(y)){
if(get(y)==get(x))rotate(y);
else rotate(x);
}
}
}
void work(int u,int v){
LL ans=;
Node *x=&tree[u];
Node *y=&tree[v];
y->fa=x;
int now=y->val;
while(x!=null){
splay(x);
int sz=(x->ch[]->size+);
ans+=1ll*query(x->val-)*sz;
update(x->val,sz);
q[++top]=x->val;
x->change(now);
x->ch[]=y;
x->pushup();
y=x;x=x->fa;
}
printf("%lld\n",ans);
for(;top;top--)clear(q[top]);
}
int main(){
null->ch[]=null->ch[]=null->fa=null;
null->id=null->size=null->val=null->lazy=;
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&val[i]);
num[i]=val[i];
}
sort(num+,num+n+);
num_cnt=unique(num+,num+n+)-num-;
for(int i=;i<=n;i++)
val[i]=lower_bound(num+,num+num_cnt+,val[i])-num;
init();
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
work(u,v);
}
return ;
}
T3,很巧妙的dp,可以见这里
Day4
liu_runda的题,开场秒T1,然后看T2,仙人掌??QAQ,还是看T3吧,果真是群论,QAQ,推了一下发现50分可以,然后又打了个表发现70也可以,最后去吧T2的20分暴力写了,别的就不会了。
T1,sb树状数组。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 100500
#define LL long long
using namespace std;
int n,m,a[N],b[N],dt[N],dx[N],dy[N],num[N<<],num_cnt;
LL num1[N<<],num2[N<<],sum1[N<<],sum2[N<<],Ans;
void add(LL *c,int x,int y){for(;x<=num_cnt;x+=x&-x)c[x]+=y;}
LL query(LL *c,int x){LL y=;for(;x;x-=x&-x)y+=c[x];return y;}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
num[++num_cnt]=a[i];
}
for(int i=;i<=n;i++){
scanf("%d",&b[i]);
num[++num_cnt]=b[i];
}
scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%d%d%d",&dt[i],&dx[i],&dy[i]);
num[++num_cnt]=dy[i];
}
sort(num+,num+num_cnt+);
num_cnt=unique(num+,num+num_cnt+)-num-;
for(int i=;i<=n;i++){
a[i]=lower_bound(num+,num+num_cnt+,a[i])-num;
add(num1,a[i],);
add(sum1,a[i],num[a[i]]);
}
for(int i=;i<=n;i++){
b[i]=lower_bound(num+,num+num_cnt+,b[i])-num;
add(num2,b[i],);
add(sum2,b[i],num[b[i]]);
Ans+=query(sum1,b[i])+(n-query(num1,b[i]))*num[b[i]];
}
printf("%lld\n",Ans);
for(int i=,x,y;i<=m;i++){
x=dx[i];y=dy[i];
if(dt[i]==){
Ans-=query(sum2,a[x])+(n-query(num2,a[x]))*num[a[x]];
add(num1,a[x],-);
add(sum1,a[x],-num[a[x]]);
a[x]=lower_bound(num+,num+num_cnt+,y)-num;
add(num1,a[x],);
add(sum1,a[x],num[a[x]]);
Ans+=query(sum2,a[x])+(n-query(num2,a[x]))*num[a[x]];
}
else{
Ans-=query(sum1,b[x])+(n-query(num1,b[x]))*num[b[x]];
add(num2,b[x],-);
add(sum2,b[x],-num[b[x]]);
b[x]=lower_bound(num+,num+num_cnt+,y)-num;
add(num2,b[x],);
add(sum2,b[x],num[b[x]]);
Ans+=query(sum1,b[x])+(n-query(num1,b[x]))*num[b[x]];
}
printf("%lld\n",Ans);
}
return ;
}
T2,联通块个数=点数-边数+环数,然后分别维护就好了,环的需要容斥每次O(大小)算,关键就是前面这个式子,想到就差不多了吧。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 100500
#define mod 998244353
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int n,m,t,w,ny,pw[N],f[N];
int e=,head[N];
struct edge{
int u,v,next;
}ed[N<<];
void add(int u,int v){
ed[e].u=u;ed[e].v=v;
ed[e].next=head[u];
head[u]=e++;
}
int fa[N],son[N],size[N],dep[N],top[N],id[N],pp[N],tot;
void dfs1(int x,int d){
dep[x]=d;
size[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x])continue;
fa[v]=x;dfs1(v,d+);
size[x]+=size[v];
if(size[v]>size[son[x]])
son[x]=v;
}
}
void dfs2(int x,int t){
top[x]=t;id[x]=++tot;pp[tot]=x;
if(son[x])dfs2(son[x],t);
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x]||v==son[x])continue;
dfs2(v,v);
}
}
bool bo[N<<],lazy[N<<];
void update(int rt){bo[rt]=lazy[rt]=;}
void pushup(int rt){bo[rt]=bo[rt<<]|bo[rt<<|];}
void pushdown(int rt){
if(!lazy[rt])return;
update(rt<<);update(rt<<|);
}
void update(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y){
update(rt);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y);
if(y>mid)update(rt<<|,mid+,r,x,y);
pushup(rt);
}
bool query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return bo[rt];
pushdown(rt);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return query(rt<<,l,mid,x,y)|query(rt<<|,mid+,r,x,y);
}
int vis[N<<];
bool check(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(query(,,n,id[top[x]],id[x]))return ;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
if(dep[x]==dep[y])return ;
if(query(,,n,id[x]+,id[y]))return ;
return ;
}
void cover(int pos,int x,int y){
int sd=dep[x]+dep[y];
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(,,n,id[top[x]],id[x]);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
sd-=*dep[x];vis[pos]=sd+;
if(dep[x]==dep[y])return ;
update(,,n,id[x]+,id[y]);
}
int fac[N],inv[N];
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int getf(int x){
if(f[x]!=-)return f[x];
int ans=;
for(int i=;i<=x;i++){
if(i&)UPD(ans,mod-1ll*C(x,i)*pw[n-i]%mod);
else UPD(ans,1ll*C(x,i)*pw[n-i]%mod);
}
return f[x]=ans;
}
int du[N<<],dv[N<<],ff[N];
int find(int x){return x==ff[x]?x:ff[x]=find(ff[x]);}
int point0,point1,edge0,edge1,cir0,cir1,ans0,ans1;
int main(){
scanf("%d%d%d%d",&n,&m,&t,&w);
fac[]=;
for(int i=;i<=n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[n]=qp(fac[n],mod-);
for(int i=n;i;i--)inv[i-]=1ll*inv[i]*i%mod;
for(int i=;i<=n;i++)ff[i]=i;
for(int i=;i<=m;i++){
scanf("%d%d",&du[i],&dv[i]);
if(find(du[i])!=find(dv[i])){
vis[i]=;
add(du[i],dv[i]);
add(dv[i],du[i]);
ff[find(du[i])]=find(dv[i]);
}
}
dfs1(,);
dfs2(,);
for(int i=;i<=m;i++)if(!vis[i])
if(check(du[i],dv[i]))
cover(i,du[i],dv[i]);
ny=qp(qp(n,t),mod-);
for(int i=;i<=n;i++)pw[i]=1ll*qp(i,t)*ny%mod;
point0=1ll*n*pw[n-]%mod;
point1=1ll*n*(-pw[n-]+mod)%mod;
memset(f,-,sizeof f);
for(int i=;i<=m;i++){
if(vis[i]){
UPD(edge0,pw[n-]);
UPD(edge1,((LL)-2ll*pw[n-]%mod+pw[n-]+mod)%mod);
if(vis[i]!=){
UPD(cir0,pw[n-vis[i]]);
UPD(cir1,getf(vis[i]));
}
}
ans0=((LL)point0-edge0+cir0+mod)%mod;
ans1=((LL)point1-edge1+cir1+mod)%mod;
if(!w)printf("%d\n",ans0);
else printf("%d\n",(ans0+ans1)%mod);
}
return ;
}
T3,暴力的话可以枚举右侧链的长度,正解是括号序,发现每次就是括号序的循环移位,所以直接裸的polya就好了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 1000500
#define mod 998244353
#define pb push_back
using namespace std;
int n,Ans,fac[N<<],inv[N<<];
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
if(m>n||m<)return ;
if(m==n||m==)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main(){
scanf("%d",&n);
fac[]=;
for(int i=;i<=*n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[*n]=qp(fac[*n],mod-);
for(int i=*n;i;i--)inv[i-]=1ll*inv[i]*i%mod; for(int i=,g;i<=*n;i++){
g=gcd(i,*n);
if(g&)continue;
UPD(Ans,C(g,g>>));
}
Ans=1ll*Ans*qp(*n,mod-)%mod;
printf("%d\n",Ans);
}
Day5
T1是同类分布原题。。。T2一开始看错题了,以为是sb题,写完发现不对,然后推出来了gcd(f[i],f[j])=f[gcd(i,j)],发现就是sb题,T3只会40分暴力。
T1,裸数位dp
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <iostream>
#define LL long long
using namespace std;
int M,a[];
LL f[][][],n;
LL dfs(int pos,int lim,int sum,int rest){
if(sum-pos*>)return ;
if(!lim&&f[pos][sum][rest]!=-)return f[pos][sum][rest];
if(!pos)return !sum&&!rest;
int up=min(sum,lim?a[pos]:);
LL ans=;
for(int i=;i<=up;i++)
ans+=dfs(pos-,lim&&i==up,sum-i,(rest*+i)%M);
if(!lim)f[pos][sum][rest]=ans;
return ans;
}
LL work(LL x){
int pos=;
while(x){a[++pos]=x%;x/=;}
LL ans=;
for(M=;M<=pos*;M++){
memset(f,-,sizeof f);
ans+=dfs(pos,,M,);
}
return ans;
}
int main(){
scanf("%lld",&n);
printf("%lld\n",work(n));
return ;
}
T2,莫比乌斯反演
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000009
#define N 1000500
using namespace std;
int prime[N],mu[N],smu[N],tot,vis[N],cnt;
void init(){
mu[]=smu[]=;
for(int i=;i<=;i++){
if(!vis[i])prime[++tot]=i,mu[i]=-;
for(int j=;j<=tot&&i*prime[j]<=;j++){
vis[i*prime[j]]=;
if(i%prime[j]==){
mu[i*prime[j]]=;
break;
}
mu[i*prime[j]]=-mu[i];
}
smu[i]=smu[i-]+mu[i];
}
}
int T,n,a[],b[],Ans;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int main(){
init();
scanf("%d",&T);
while(T--){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
b[i]=a[i]>>;
}
sort(a+,a+n+);
Ans=;
for(int i=,nowa,nowb,nxt;i<=a[];i=nxt+){
nowa=nowb=;
nxt=N;
for(int j=;j<=n;j++){
nxt=min(nxt,a[j]/(a[j]/i));
nowa=1ll*nowa*(a[j]/i)%mod;
nowb=1ll*nowb*(b[j]/i)%mod;
}
nowa=1ll*(smu[nxt]-smu[i-]+mod)*(nowa+nowb)%mod;
UPD(Ans,nowa);
}
printf("%d\n",Ans);
}
return ;
}
T3,mex的线段树做法+扫描线
#include <bits/stdc++.h>
#define N 100500
using namespace std;
set<int> ss;
int n,m,a[N],hd[N],nxt[N],val[N];
int mn[N<<],mx[N<<],lz[N<<];
void update(int rt,int x){mx[rt]=lz[rt]=x;}
void pushup(int rt){mx[rt]=max(mx[rt<<],mx[rt<<|]);}
void pushdown(int rt){if(~lz[rt]){update(rt<<,lz[rt]);update(rt<<|,lz[rt]);lz[rt]=-;}}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y&&mn[rt]>z){
update(rt,z);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
pushup(rt);
}
int query(int rt,int l,int r,int x,int y,int z){
if(l==r){if(mx[rt]>z)return l;return ;}
pushdown(rt);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y,z);
if(x>mid)return query(rt<<|,mid+,r,x,y,z);
if(mx[rt<<]>z)return query(rt<<,l,mid,x,y,z);
return query(rt<<|,mid+,r,x,y,z);
}
double minn[N<<],Ans[N];
void update(int rt,int l,int r,int x,double y){
if(l==r){minn[rt]=min(minn[rt],y);return;}
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y);
else update(rt<<|,mid+,r,x,y);
minn[rt]=min(minn[rt<<],minn[rt<<|]);
}
double query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return minn[rt];
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return min(query(rt<<,l,mid,x,y),query(rt<<|,mid+,r,x,y));
}
int tot;
struct data{
int l,r;double f;
data(){}
data(int a,int b,double c){l=a;r=b;f=c;}
}d[N<<];
struct qr{
int l,r,id;
}q[N];
bool cmp1(data a,data b){return a.r<b.r;}
bool cmp2(qr a,qr b){return a.r<b.r;}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<=n+;i++)hd[i]=n+;
for(int i=n;i;i--){
nxt[i]=hd[a[i]];
hd[a[i]]=i;
}
memset(lz,-,sizeof lz);
memset(mn,0x3f,sizeof mn);
memset(mx,0x3f,sizeof mx);
for(int i=,now=;i<=n+&&now<=n;i++)if(hd[i]>now){
update(,,n,now,hd[i]-,i);
ss.insert(now);val[now]=i;
now=hd[i];
}
set<int> :: iterator it,iit;
for(int i=,pos;i<=n;i++){
pos=query(,,n,i,nxt[i]-,a[i]);
if(!pos)continue;
update(,,n,pos,nxt[i]-,a[i]);
it=ss.lower_bound(pos);
if(it==ss.end())continue;
int r=*it;iit=it;it++;
d[++tot]=data(i,r,(double)((r-i+)+-val[r])/1.0/((r-i+)++val[r]));
if(r==i&&(*it)>i+){
val[i+]=a[i];
ss.insert(i+);
}
if(it==ss.end()||(*it)>=nxt[i]){
if(nxt[i]<=n){val[nxt[i]]=val[r];ss.insert(nxt[i]);}
val[r]=a[i];continue;
}val[r]=a[i];
for(;;){
r=*it;iit=it;it++;ss.erase(iit);
d[++tot]=data(i,r,(double)((r-i+)+-val[r])/1.0/((r-i+)++val[r]));
if(it==ss.end()||(*it)>=nxt[i]){
if(nxt[i]<=n){val[nxt[i]]=val[r];ss.insert(nxt[i]);}
break;
}
}
}
for(int i=;i<=m;i++){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(d+,d+tot+,cmp1);
sort(q+,q+m+,cmp2);
memset(minn,0x3f,sizeof minn);
for(int i=;i<=n*;i++)minn[i]=1e9;
for(int i1=,i2=;i2<=m;){
if(i1<=tot&&d[i1].r<=q[i2].r){update(,,n,d[i1].l,d[i1].f);i1++;}
else {Ans[q[i2].id]=query(,,n,q[i2].l,q[i2].r);i2++;}
}
for(int i=;i<=m;i++){
if(Ans[i]>)puts("1.00000000");
else printf("%0.8f\n",Ans[i]);
}
return ;
}
Day6
先看T1,我会链的!我...还会菊花图的!我...还会5以内的puts样例!知足了。再看T2,我会O(n^2)暴力!别的,不会了,不存在的。
T1,直径的期望,不会,QAQ,嘤嘤嘤。
T2,矩阵优化dp,各种乱搞。
T3,图论题,强联通分量,dp。
Day7
三道题都不会做。。T1想了好久没想出来,T2O(n^3)暴力想偏了,就优化失败了,T3只会暴力,凉凉。
T1,小dp,先处理出i段覆盖全部的方案数,然后就可以处理出放了i个才全部覆盖的方案数,然后直接统计答案即可。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 1000500
using namespace std;
int n,K,f[N],fac[N],inv[N],g[N],h[N],Ans;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int work(int x,int y){
if(x*K<y)return ;
int ans=;
for(int i=;i<=x&&(i*K+x)<=y;i++){
if(i&)UPD(ans,mod-1ll*C(x,i)*C(y-i*K-,x-)%mod);
else UPD(ans,1ll*C(x,i)*C(y-i*K-,x-)%mod);
}
return ans;
}
int main(){
scanf("%d%d",&n,&K);
for(int i=fac[]=;i<=n-K+;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[n-K+]=qp(fac[n-K+],mod-);
for(int i=n-K+;i;i--)inv[i-]=1ll*inv[i]*i%mod;
for(int i=;i<=n-K+;i++){
if(K==)f[i]=C(i-,n-i-);
else f[i]=work(i-,n-K);
g[i]=1ll*f[i]*fac[i]%mod;
}
for(int i=;i<=n-K+;i++){
h[i]=(g[i]-1ll*g[i-]*(n-K+-(i-))%mod+mod)%mod;
UPD(Ans,1ll*h[i]*(n-K+-i)%mod*fac[n-K+-i]%mod);
}
printf("%d\n",Ans);
return ;
}
T2,考虑O(n^3)暴力,即枚举左右端点,只有L<=a<=R&&l<=L&&R<=r才可以作出贡献,然后发现每个人做出贡献的区间是确定的,然后直接扫加线段树维护就可以。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 100500
#define pb push_back
using namespace std;
vector<int> V1[*N],V2[*N];
struct data{int l,a,r;}d[N];
bool cmp(data a,data b){return a.a<b.a;}
int num[*N],num_cnt,l[N],a[N],r[N],n,Ans;
int mx[*N],lz[*N];
void update(int rt,int x){mx[rt]+=x;lz[rt]+=x;}
void pushup(int rt){mx[rt]=max(mx[rt<<],mx[rt<<|]);}
void pushdown(int rt){if(lz[rt]){update(rt<<,lz[rt]);update(rt<<|,lz[rt]);lz[rt]=;}}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){
update(rt,z);
return ;
}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
pushup(rt);
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d%d%d",&d[i].l,&d[i].a,&d[i].r);
num[++num_cnt]=d[i].l;
num[++num_cnt]=d[i].a;
num[++num_cnt]=d[i].r;
}
sort(d+,d+n+,cmp);
sort(num+,num+num_cnt+);
num_cnt=unique(num+,num+num_cnt+)-num-;
for(int i=;i<=n;i++){
l[i]=lower_bound(num+,num+num_cnt+,d[i].l)-num;
a[i]=lower_bound(num+,num+num_cnt+,d[i].a)-num;
r[i]=lower_bound(num+,num+num_cnt+,d[i].r)-num;
V1[l[i]].pb(i);
V2[a[i]].pb(i);
}
for(int i=;i<=num_cnt;i++){
for(int j=;j<V1[i].size();j++)
update(,,num_cnt,a[V1[i][j]],r[V1[i][j]],);
Ans=max(Ans,mx[]);
for(int j=;j<V2[i].size();j++)
update(,,num_cnt,a[V2[i][j]],r[V2[i][j]],-);
}
printf("%d\n",Ans);
return ;
}
T3,还是线段树,我们枚举每个因子,看是它的倍数的数,如果多于1个,就说明有几段区间的答案要大于等于它,然后还是线段树。可以是最简单的jry线段树,或者骗分线段树。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 200500
#define LL long long
using namespace std;
int n,maxa,tot,a[N],cnt[N],mx1[N],mx2[N],mn1[N],mn2[N];
LL mx[N<<],mn[N<<],lazy[N<<],sum[N<<],Ans;
struct data{
int a,b,c;
data(){}
data(int x,int y,int z){a=x;b=y;c=z;}
}d[*N];
bool cmp(data a,data b){return a.a<b.a;}
void add(int x,int y){
cnt[x]++;
if(y>mx1[x]){mx2[x]=mx1[x];mx1[x]=y;}
else if(y>mx2[x]){mx2[x]=y;}
if(y<mn1[x]){mn2[x]=mn1[x];mn1[x]=y;}
else if(y<mn2[x]){mn2[x]=y;}
}
void update(int rt,int l,int r,LL x){
mx[rt]=mn[rt]=lazy[rt]=x;
sum[rt]=x*(r-l+);
}
void pushdown(int rt,int l,int r){
if(lazy[rt]){
int mid=(l+r)>>;
update(rt<<,l,mid,lazy[rt]);
update(rt<<|,mid+,r,lazy[rt]);
lazy[rt]=;
}
}
void pushup(int rt){
mn[rt]=min(mn[rt<<],mn[rt<<|]);
mx[rt]=max(mx[rt<<],mx[rt<<|]);
sum[rt]=sum[rt<<]+sum[rt<<|];
}
void update(int rt,int l,int r,int x,int y,LL z){
if(mn[rt]>=z)return ;
if(x<=l&&r<=y&&z>=mx[rt]){
update(rt,l,r,z);
return ;
}
if(l==r)return ;
pushdown(rt,l,r);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
pushup(rt);
}
LL query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return sum[rt];
pushdown(rt,l,r);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return query(rt<<,l,mid,x,y)+query(rt<<|,mid+,r,x,y);
}
int main(){
scanf("%d",&n);
memset(mn1,0x3f,sizeof mn1);
memset(mn2,0x3f,sizeof mn2);
memset(mx1,,sizeof mx1);
memset(mx2,,sizeof mx2);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
maxa=max(maxa,a[i]);
for(int j=;j*j<=a[i];j++)if(a[i]%j==){
add(j,i);
if(j*j!=a[i])add(a[i]/j,i);
}
}
for(int i=;i<=maxa;i++)if(cnt[i]>){
if(mn1[i]+<mx1[i])d[++tot]=data(mn1[i]+,mx1[i]-,i);
if(mn2[i]<n)d[++tot]=data(mn2[i]+,n,i);
if(mx2[i]>)d[++tot]=data(,mx2[i]-,i);
}
sort(d+,d+tot+,cmp);
for(int i=,j=;i<=n;i++){
for(;j<=tot&&d[j].a==i;j++)update(,,n,d[j].a,d[j].b,d[j].c);
Ans+=query(,,n,i,n);
}
printf("%lld\n",Ans);
return ;
}
接着联考?
先看题,感觉不是很难维护,想了想发现可以对于%每位意义下处理一个线段树,然后每次区间加相当于循环移位,然后发现删除会出锅,就又写了个Treap,2333,然后看T2,原题,可是我当时没改,接着写暴力,有40分,看T3,感觉做过,但是记不太请了,最后暴力没调完。
T1,对于每位维护一个线段树(树状数组),全局维护一个平衡树(map)就好了。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define LL long long
#define N 555555
#define B 30
using namespace std;
struct Bit{
vector<int> c;
int n;
void init(int x){n=x;c.resize(x+);}
void update(int x,int y){x++;for(;x<=n;x+=x&-x)c[x]+=y;}
int query(int x){int y=;for(;x;x-=x&-x){y+=c[x];}return y;}
int query(int x,int y){return query(y+)-query(x);}
}BT[];
LL add;
map<LL,int> mm;
int n,pw[B],cir[B];
void insert(int x){
for(int i=;~i;i--)
BT[i].update((x-cir[i]+pw[i+])%pw[i+],);
}
void del(int p,int x){
for(int i=;~i;i--)
BT[i].update((x-cir[i]+pw[i+])%pw[i+],-p);
}
void update(int x){
for(int i=;i<=;i++)
(cir[i]+=x)&=(pw[i+]-);
}
int query(int x){
int a=(pw[x]-cir[x]+pw[x+])%pw[x+];
int b=(pw[x+]--cir[x]+pw[x+])%pw[x+];
if(a<=b)return BT[x].query(a,b);
return BT[x].query(a,pw[x+]-)+BT[x].query(,b);
}
int main(){
for(int i=pw[]=;i<=;i++)pw[i]=pw[i-]<<;
for(int i=;i<=;i++)BT[i-].init(pw[i]);
scanf("%d",&n);
for(int i=,o,x;i<=n;i++){
scanf("%d%d",&o,&x);
if(o==){
insert(x);
if(mm.count(x-add))mm[x-add]++;
else mm[x-add]=;
}
if(o==&&mm.count(x-add))del(mm[x-add],x),mm[x-add]=;
if(o==)update(x),add+=x;
if(o==)printf("%d\n",query(x));
}
return ;
}
T2,原题,搞出 $\sum_{i=1,(i,x)=1}^{n}{\mu{(i)}}$和$\sum_{i=1,(i,x)=1}^{n}{\lfloor{\frac{n}{i}}\rfloor}$,这个可以枚举质数来处理,复杂度$O(n \sqrt{n} logn)$。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <cmath>
#define N 100500
using namespace std;
int A,B,C,mu[N],d[N],s[N],mk[N],mp[N],prime[N],tot;
int cnt,pp[N],bb[N],cc[N];
unsigned int f[N],g[N],F[N],G[N],ans[N],Ans;
bool vis[N],bo[N];
void init(){
mu[]=;d[]=;s[]=;
for(int i=;i<=;i++){
if(!vis[i]){
d[i]=;mk[i]=;
mu[i]=-;mp[i]=i;s[i]=i;
prime[++tot]=i;
}
for(int j=;j<=tot&&i*prime[j]<=;j++){
vis[i*prime[j]]=;
mp[i*prime[j]]=prime[j];
if(i%prime[j]==){
mk[i*prime[j]]=mk[i]+;
d[i*prime[j]]=d[i]/(mk[i]+)*(mk[i]+);
mu[i*prime[j]]=;
s[i*prime[j]]=s[i];
break;
}
mk[i*prime[j]]=;
d[i*prime[j]]=d[i]*d[prime[j]];
mu[i*prime[j]]=-mu[i];
s[i*prime[j]]=s[i]*prime[j];
}
}
for(int i=;i<=;i++){
f[i]=f[i-]+mu[i];
g[i]=g[i-]+d[i];
}
}
unsigned int calc(int x){
if(bo[s[x]])return ans[s[x]];
for(int i=;i<=cnt;i++)F[pp[i]]=f[pp[i]],G[pp[i]]=g[pp[i]];
int y=s[x];
while(y>){
for(int i=;i<=cnt;i++)F[pp[i]]+=F[pp[i]/mp[y]];
for(int i=cnt;i>=;i--)G[pp[i]]-=G[pp[i]/mp[y]];
y/=mp[y];
}
int ret=;
for(int i=;i<=cnt&&pp[i]<=B;i++)
ret+=(F[pp[i]]-F[pp[i-]])*G[bb[i]]*G[cc[i]];
bo[s[x]]=;
return ans[s[x]]=ret;
}
int main(){
init();
scanf("%d%d%d",&A,&B,&C);
if(A>B)swap(A,B);
if(A>C)swap(A,C);
if(B>C)swap(B,C);
for(int i=,j;i<=C;i=j+){
if(i<=B)j=min(B/(B/i),C/(C/i));
else j=C/(C/i);
pp[++cnt]=j;bb[cnt]=B/i;cc[cnt]=C/i;
}
for(int i=;i<=A;i++)
Ans+=(A/i)*calc(i);
printf("%lld\n",1ll*Ans);
return ;
}
T3,也是原题,拆成斯特林数,枚举联通块集合做贡献。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define L 131072
#define N 133333
#define mod 1004535809
using namespace std;
int n,m,T,mx=;
int h[N],g[N],f[][N],ans[N][],t[][N],S[][];
int fac[N],inv[N],tmp1[N],tmp2[N],len,ny,rev[N],pw[N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void ntt(int *a,int o){
register int i,j,k,t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);++j){
t=1ll*pw[o==?L/k*j:L-L/k*j]*a[i+j+(k>>)]%mod;
a[i+j+(k>>)]=(a[i+j]-t+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
if(o==-)for(i=;i<len;++i)a[i]=1ll*a[i]*ny%mod;
}
void init(int n){
for(len=;len<=n;len<<=);
ny=qp(len,mod-);
for(register int i=;i<len;++i){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
}
void cdq(int l,int r){
if(l==r){
if(l)UPD(g[l],h[l]);
return ;
}
register int mid,len1,len2,i,j;
mid=(l+r)>>;
cdq(l,mid);
if(r-l>){
len1=mid-l+,len2=r-l+;
init(len1+len2);
for(i=l;i<=mid;++i)
tmp1[i-l]=1ll*g[i]*inv[i-]%mod;
for(i=mid-l+;i<len;++i)tmp1[i]=;
for(i=;i<=r-l;++i)
tmp2[i]=1ll*h[i]*inv[i]%mod;
for(i=r-l+;i<len;++i)tmp2[i]=;
ntt(tmp1,);ntt(tmp2,);
for(i=;i<len;++i)tmp2[i]=1ll*tmp1[i]*tmp2[i]%mod;
ntt(tmp2,-);
for(i=mid+;i<=r;++i)
UPD(g[i],mod-1ll*fac[i-]*tmp2[i-l]%mod);
}
else{
for(i=mid+;i<=r;++i)
for(j=l;j<=mid;++j)
UPD(g[i],mod-1ll*g[j]*h[i-j]%mod*C(i-,j-)%mod);
}
cdq(mid+,r);
}
void init(){
register int i,j,k;
register int dan=qp(,(mod-)/L);
pw[]=;
for(i=;i<=L;++i)pw[i]=1ll*pw[i-]*dan%mod;
S[][]=;
for(i=;i<=;++i){
S[i][]=;
for(j=;j<=i;++j)
S[i][j]=(S[i-][j-]+S[i-][j]*j%mod)%mod;
}
for(i=fac[]=;i<=mx;++i)fac[i]=1ll*fac[i-]*i%mod;
inv[mx]=qp(fac[mx],mod-);
for(i=mx;i;--i)inv[i-]=1ll*inv[i]*i%mod; for(i=h[]=;i<=mx;++i)h[i]=qp(,1ll*i*(i-)/%(mod-));
cdq(,mx);//getg! init(mx<<);
for(i=;i<=mx;++i)f[][i]=g[i];
memset(tmp1,,sizeof tmp1);
for(i=;i<len;++i)tmp1[i]=1ll*g[i]*inv[i-]%mod;
ntt(tmp1,);
for(j=;j<=;++j){
memset(tmp2,,sizeof tmp2);
for(i=j-;i<=mx;++i)tmp2[i]=1ll*f[j-][i]*inv[i]%mod;
ntt(tmp2,);
for(i=;i<len;++i)tmp2[i]=1ll*tmp1[i]*tmp2[i]%mod;
ntt(tmp2,-);
for(i=j;i<=mx;++i)f[j][i]=1ll*tmp2[i]*fac[i-]%mod;
} memset(tmp1,,sizeof tmp1);
for(i=;i<=mx;++i)tmp1[i]=1ll*h[i]*inv[i]%mod;
ntt(tmp1,);
for(k=;k<=;++k){
memset(tmp2,,sizeof tmp2);
for(i=;i<=mx;++i)tmp2[i]=1ll*f[k][i]*inv[i]%mod;
ntt(tmp2,);
for(i=;i<len;++i)tmp2[i]=1ll*tmp1[i]*tmp2[i]%mod;
ntt(tmp2,-);
for(i=;i<=mx;++i)t[k][i]=1ll*fac[i]*tmp2[i]%mod;
} for(i=;i<=mx;++i){
ans[i][]=;
for(j=;j<=;++j){
for(k=;k<=j;++k)
UPD(ans[i][j],1ll*t[k][i]*S[j][k]%mod*fac[k]%mod);
ans[i][j]=1ll*ans[i][j]*qp(h[i],mod-)%mod;
}
}
}
int main(){
init();
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
printf("%d\n",ans[n][m]);
}
return ;
}
6.22
先看T1,欧拉回路?2的幂?不会。T2,又是组合数???T3,nq暴力很好想的样子。最后T1暴力写挂,T2暴搜20,T3暴力40。
T1,看到图先想生成树,看到欧拉回路先想度数限制,然后就是一个裸的树形dp。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 500500
#define mod 1000000007
using namespace std;
int n,m,fa[N],w[N],in[N],ans;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int e=,head[N];
struct edge{
int u,v,w,next;
}ed[N<<];
void add(int u,int v,int w){
ed[e].u=u;ed[e].v=v;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
}
void dfs(int x,int fa){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
if(in[v]){
(ans+=ed[i].w)%=mod;
in[x]^=;in[v]=;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)fa[i]=i;
w[]=;
for(int i=,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
in[u]^=;in[v]^=;
w[i]=(w[i-]*)%mod;
(ans+=w[i])%=mod;
if(find(u)!=find(v)){
fa[find(u)]=find(v);
add(u,v,w[i]);
add(v,u,w[i]);
}
}
dfs(,);
printf("%d\n",ans);
}
T2,杨式矩阵,钩子定理。复杂度$O(T sqrt{n})$
#include <bits/stdc++.h>
#define mod 1000000007
using namespace std;
int T,a,b,c,d,n1,n2,m1,m2,ans,fac[],inv[];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)if(b&)c=1ll*c*a%mod;
return c;
}
int calc(int n,int m,int x){
int ans=;if(n>m)swap(n,m);
for(int i=;i<n;i++)ans=1ll*ans*inv[m+i+x]%mod*fac[i+x]%mod;
return ans;
}
int main(){
for(int i=fac[]=;i<=;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[]=qp(fac[],mod-);
for(int i=;i>=;i--)inv[i-]=1ll*inv[i]*i%mod;
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&a,&b,&c,&d);
n1=a-b+;m1=b;n2=c-d+;m2=d;
if(n1>n2||(n1==n2&&m1>m2))swap(n1,n2),swap(m1,m2);
if(m2>=m1)ans=1ll*fac[n2*m2]*calc(n2,m2,)%mod;
else ans=1ll*fac[n1*m1+(n2-n1)*m2]*calc(n1,m1-m2,)%mod*calc(n2-n1,m2,)%mod*calc(m2,n1,(n2-n1)+(m1-m2))%mod;
printf("%d\n",ans);
}
return ;
}
T3,对于取min/max的题没有优化空间时可以看看决策位置的特点。这题后缀min是关键。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <set>
#define N 200500
using namespace std;
int n,m,a[N],mn,last,vis[N];
long long sum[],sums[];
struct data{
int pos,val;
data(int x,int y){pos=x;val=y;}
};
struct cmp{
bool operator () (const data & a,const data & b){
if(a.pos==b.pos)return a.val<b.val;
return a.pos<b.pos;
}
};
set<data,cmp> ss;
set<data,cmp> :: iterator it,iit;
signed main(){
scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&a[i]);
mn=0x7fffffff;
last=;
ss.insert(data(-,-));
for(int i=n;i>=;i--){
if(mn>a[i]){
mn=a[i];
ss.insert(data(i,a[i]));
sums[i&]+=last;
last=a[i];
vis[i]=;
}
else sum[i&]+=a[i];
}
sums[]+=last;
printf("%lld\n",sum[]+sums[]);
scanf("%d",&m);
for(int i=,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
if(vis[x]){
for(last=,it=ss.find(data(x,a[x]));;){
sums[it->pos&]-=last;
if(it->val<a[x]-y){
sums[it->pos&]+=a[x]-y;
break;
}
last=it->val;
if(it->val!=a[x]){
sum[it->pos&]+=it->val;
vis[it->pos]=;
}
iit=it;it--;ss.erase(iit);
}
ss.insert(data(x,a[x]-y));
}
else{
it=ss.lower_bound(data(x,a[x]));
if(it->val<=a[x]-y)sum[x&]-=y;
else{
sums[x&]+=it->val;
sum[x&]-=a[x];
for(last=it->val,it--;;){
sums[it->pos&]-=last;
if(it->val<a[x]-y){
sums[it->pos&]+=a[x]-y;
break;
}
sum[it->pos&]+=it->val;
last=it->val;
vis[it->pos]=;
iit=it;it--;ss.erase(iit);
}
ss.insert(data(x,a[x]-y));
vis[x]=;
}
}
a[x]-=y;
printf("%lld\n",sum[]+sums[]);
}
return ;
}
NOI倒计时24days
6.23
考试一直再想T1,发现了性质之后没有想明白,差一点,QAQ,后两题只会暴力,T2真的是多项式,不过有点nb,T3构造的思路还是比较巧妙的。
现在感觉每天都是可以AK的,但是每天都做不到,深入思考,思考。
T1,发现每个位置要么是每行都相等,要么是每列都相等,然后状压,行列独立可以分开考虑,全1/0需要容斥一下。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
using namespace std;
int C[][],n,m,h,w,Ans,ret,cnth[],cntw[];
int tot,id[][],s1[],s2[],r1[],r2[],ch[][][],cw[][][];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int main(){
scanf("%d%d%d%d",&n,&m,&h,&w);
if(h>w)swap(n,m),swap(h,w);
register int i,j,k,l;
for(i=;i<=;++i){
C[i][]=;
for(j=;j<=i;++j)
C[i][j]=(C[i-][j-]+C[i-][j])%mod;
}
for(i=;i<=h;++i){
cnth[i]=(n-i)/h+;
for(j=;j<=;++j)
for(k=;k<=;++k)ch[i][j][k]=qp(C[j][k],cnth[i]);
}
for(i=;i<=w;++i){
cntw[i]=(m-i)/w+;
for(j=;j<=;++j)
for(k=;k<=;++k)cw[i][j][k]=qp(C[j][k],cntw[i]);
}
for(i=;i<=h;++i)
for(j=;j<=w;++j)id[i][j]=++tot;
for(i=;i<(<<h*w);++i){
for(j=;j<=h;++j)s1[j]=;
for(j=;j<=w;++j)s2[j]=;
for(j=;j<=h;++j){
for(k=;k<=w;++k){
if(i&(<<(id[j][k]-)))s1[j]++;
else s2[k]++;
}
}
for(j=;j<=h;++j){
r1[j]=;
for(k=;k<=s1[j];++k)
UPD(r1[j],ch[j][s1[j]][k]);
}
for(j=;j<=w;++j){
r2[j]=;
for(k=;k<=s2[j];++k)
UPD(r2[j],cw[j][s2[j]][k]);
for(k=;k<=s2[j];++k){
for(l=;l<=s2[j]-k;++l){
if(k&)UPD(r2[j],mod-1ll*C[s2[j]][k]*(<<k)*cw[j][s2[j]-k][l]%mod);
else UPD(r2[j],1ll*C[s2[j]][k]*(<<k)*cw[j][s2[j]-k][l]%mod);
}
}
}
ret=;
for(j=;j<=h;++j)ret=1ll*ret*r1[j]%mod;
for(j=;j<=w;++j)ret=1ll*ret*r2[j]%mod;
UPD(Ans,ret);
}
printf("%d\n",Ans);
return ;
}
T2,好nb的多项式啊,找规律?插值?找相同点,这个就是%lcm。
#pragma GCC optimize ("O3")
#include <cstdio>
#define LL long long
#define mod 1000000007
using namespace std;
int n,mx,a[],Ans,f[],l[],L[][],c[];
LL le,ri;
void UPD(int & a,const int & b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
inline int gcd(const int & a,const int & b){return !b?a:gcd(b,a%b);}
inline int lcm(const int & a,const int & b){return 1ll*a*b/gcd(a,b);}
inline int getf(const LL & x,const int & k){
if(x-k<)return ;
register int ans=,p=1ll*(x-k)/mx%mod,i,now;
for(i=,now=;i<=n;i++,now=1ll*now*p%mod)
UPD(ans,1ll*c[i]*now%mod);
return ans;
}
int main(){
register int i,j,k,now;
scanf("%d",&n);
for(i=mx=;i<=n;++i){
scanf("%d",&a[i]);
mx=lcm(mx,a[i]);
}
for(i=f[]=l[]=;i<=n;++i)
for(j=a[i];j<(n+)*mx;++j)UPD(f[j],f[j-a[i]]);
for(i=mx;i<(n+)*mx;++i)UPD(f[i],f[i-mx]);
for(i=;i<=n;++i){
for(j=i;~j;--j){
UPD(l[j+],l[j]);
l[j]=1ll*l[j]*(mod-i)%mod;
}
}
for(i=,now=;i<=n;++i,now=){
if(!i)for(j=;j<=n;++j)L[i][j]=l[j+];
else{
for(j=;j<=n+;++j)L[i][j]=l[j];
for(j=;j<=n;++j){
L[i][j]=1ll*L[i][j]*qp(mod-i,mod-)%mod;
UPD(L[i][j+],mod-L[i][j]);
}
}
for(j=;j<=n;++j)if(i!=j)now=1ll*now*qp(i-j+mod,mod-)%mod;
for(j=;j<=n;++j)L[i][j]=1ll*L[i][j]*now%mod;
}
scanf("%lld%lld",&le,&ri);
for(i=;i<mx;++i){
for(j=;j<=n;++j)c[j]=;
for(j=;j<=n;++j)
for(k=;k<=n;++k)
UPD(c[k],1ll*f[j*mx+i]*L[j][k]%mod);
UPD(Ans,(getf(ri,i)-getf(le-,i)+mod)%mod);
}
printf("%d\n",Ans);
return ;
}
T3,先搞出两排点,设以两个末尾结尾的拓扑序是(x,y),我们往第一排后面加一个点,让原来的第一排的最后一个和第二排的倒数第二个连向他可以构造出(x+y,y),然后枚举搜就行
#include <cstdio>
using namespace std;
int n,e,qy[],qx[],tx,ty,du[],dv[];
void add(int u,int v){du[++e]=u;dv[e]=v;}
int cnt,nx,ny,s0,t0,t1,s1,tot;
int work(int x,int y){
cnt=;
tx=ty=;
while(x!=||y!=){
if(!x||!y)return ;
if(x<y){qy[++ty]=y;y=y-x;}
else{qx[++tx]=x;x=x-y;}
if(++cnt>)return ;
}
nx=,ny=,s0=,t0=,t1=,s1=,tot=;
while(tx||ty){
if(tx&&qx[tx]==nx+ny){
tot++;tx--;nx=nx+ny;
add(s1,tot);add(t0,tot);
s0=s1;s1=tot;
}
else{
tot++;ty--;ny=nx+ny;
add(t1,tot);add(s0,tot);
t0=t1;t1=tot;
}
}
printf("%d %d\n0 1\n 0 2\n",tot+,e+);
for(int i=;i<=e;i++)
printf("%d %d\n",du[i],dv[i]);
return ;
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++)if(work(i,n-i))return ;
}
NOI倒计时23days
今天下午打了bzoj的月赛,少打一个多小时,最后是4题,E题差30s,QAQ。
先看了眼榜,确定了先做ACG的策略,先切了A,然后证明了一下G的小结论A了,然后C题有显然的做法,交,TLE,wtf???卡了一会常,刚想打个fft,进行了最后一波优化A了,然后看B,一眼没什么思路,倒是E题因子的思路很显然,还是决定去刚B,手玩了一会,发现2的话都往叶子上放是最优的,然后想怎么扩展,一开始想往上爬,手玩出了反例,然后发现只要去了叶子再做一遍就行,先打了个带log的,T了,发现是我sb,改了就A了。然后去写E,倒是比较好写,写完交,TLE,优化了一波,啥,考试结束了?在外面交了一发,A了。
这两天可以看看历届的noi和uoj,loj的noi模拟,比较清楚的题就不用具体实现了,突然发现上午打的网格没存,QAQ。。。让我冷静一下。。。QAQ
然后的话cf的话可以刷一刷div1的后几题,有什么问题的话可以去找Amphetamine或着qaz。
加油吧!
NOI倒计时22days
考试,看T1,裸burn...什么玩意???感觉不是很不可做,于是直接开始刚,刚了好久推出来了两个组合数的式子,和暴力拍上了,很稳...后两题都不太会做啊,打了两题的暴力,最后T1数组开小了,草,T3tarjan打错了,草。。。。。
T1,l为偶数的话可以置换出所有,只要颜色数相同就可以。l为奇数时可以置换出一半,需要至少有一种颜色染了至少两个球。l=n直接burnside。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
using namespace std;
int n,m,l,ans,fac[],inv[];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
namespace work1{
void Main(){
for(int i=,x;i<=n;i++){
x=gcd(i,n);
UPD(ans,qp(m,x));
}
ans=1ll*ans*qp(n,mod-)%mod;
printf("%d\n",ans);
}
}
namespace work2{
void Main(){
ans=(C(n+m-,m-)+C(m,n))%mod;
printf("%d\n",ans);
}
}
namespace work3{
void Main(){
ans=C(n+m-,m-);
printf("%d\n",ans);
}
}
int main(){
scanf("%d%d%d",&n,&m,&l);
if(l==n){work1::Main();return ;}
fac[]=;
for(int i=;i<=n+m;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[n+m]=qp(fac[n+m],mod-);
for(int i=n+m;i>=;i--)inv[i-]=1ll*inv[i]*i%mod;
if(l&){work2::Main();return ;}
else{work3::Main();return ;}
}
T2,发现问题的关键点是中间那行的状态,于是f[i][j],g[i][j]分别表示i列中间那个格子是竖着/横着填的在前i列中第j个填的方案数,然后转移,dp数组表示方案数,没问题啊...
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 2005
#define mod 1000000007
using namespace std;
int a[][N],all,n,f[][N<<],g[][N<<],sf[][N<<],sg[][N<<],Ans,fac[N<<],inv[N<<];
char s[N];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int A(int n,int m){return m?(m==?n:1ll*n*(n-)%mod):;}
int main(){
register int i,j,x,ii;
scanf("%d",&n);
for(i=;i<=;++i){
scanf("%s",s+);
for(j=;j<=n;++j)if(s[j]=='x'){
a[i][j]=;Ans++;
if(i&&a[i][j-]){puts("");return ;}
}
}
for(i=fac[]=;i<=Ans;++i)fac[i]=1ll*fac[i-]*i%mod;
inv[Ans]=qp(fac[Ans],mod-);
for(i=Ans;i>=;--i)inv[i-]=1ll*inv[i]*i%mod;
Ans=fac[Ans];
if(a[][]||a[][n]||a[][]||a[][n]){puts("");return ;}
for(i=;i<=n;++i)if(a[][i]){
all=ii=sg[][]=;sf[][]=;
for(;a[][i];++i){
ii^=;sf[ii][]=sg[ii][]=;
x=a[][i]+a[][i];
for(j=;j<=all+x+;++j){
f[ii][j]=(1ll*A(j-,x)*sf[ii^][all]%mod+1ll*A(j-,x)*(sg[ii^][all]-((j-x->=)?sg[ii^][j-x-]:)+mod)%mod)%mod;
g[ii][j]=(x)?((x==)?(1ll*(all+-j)*sf[ii^][j-]%mod):((2ll*(j-)*(j->=?sf[ii^][j-]:)%mod*(all+-j)%mod+1ll*sf[ii^][j-]*A(all+-j,))%mod)):();
sf[ii][j]=(sf[ii][j-]+f[ii][j])%mod;sg[ii][j]=(sg[ii][j-]+g[ii][j])%mod;
}
all+=x+;
}
Ans=1ll*Ans*(sf[ii][all]+sg[ii][all])%mod*inv[all]%mod;
}
printf("%d\n",Ans);
return ;
}
T3,暴搜是O(n^4)的,但是每行的f是单调的,可以优化到O(n^3)。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 405
#define inf 100000000
using namespace std;
int vis[N][N],tot,f[N][N],n,m,a[N][N];
char s[N];
int dfs(int x,int y,int o){
if(vis[x][y]==)return f[][];
if(vis[x][y]==)return ;
vis[x][y]=;
int cnt=;
if(!o){
if(y<m)cnt+=dfs(x,y+,);
if(a[x][y]==)if(x<n)cnt+=dfs(x+,y,a[x+][y]?:);
if(a[x][y]==)if(x>)cnt+=dfs(x-,y,a[x-][y]?:);
}
else{
if(y>)cnt+=dfs(x,y-,);
if(a[x][y]==)if(x<n)cnt+=dfs(x+,y,a[x+][y]?:);
if(a[x][y]==)if(x>)cnt+=dfs(x-,y,a[x-][y]?:);
}
vis[x][y]=;
return cnt>inf?inf:cnt;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++){
scanf("%s",s+);
for(int j=;j<=m;j++)
if(s[j]=='N')a[i][j]=;
}
memset(f,0x3f,sizeof f);
for(int i=;i<=n;i++){
tot=;memset(vis,,sizeof vis);
for(int j=;j<=m;j++){
tot+=dfs(i,j,);
if(tot>=inf)break;
f[i][j]=min(f[i][j],tot);
}
tot=;memset(vis,,sizeof vis);
for(int j=m;j>=;j--){
tot+=dfs(i,j,);
if(tot>=f[i][j])break;
f[i][j]=tot;
}
}
for(int i=;i<=n;i++){
for(int j=;j<=m;j++){
if(f[i][j]<inf)printf("%d ",f[i][j]);
else printf("-1 ");
}puts("");
}
return ;
}
NOI倒计时21days
T1,dfs序裸题,T2,看起来像是斜率优化什么的,T3,fft!先写T1,写完拍上了,再写T3,写完拍上了,想T2,想到一个log2的做法,最后没调出来,貌似还会炸long long。
T1add(int rt,int l,int r,int x),应该是LL x,炸了两个包,QAQ。
T1,大力讨论一波即可。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 300500
#define LL long long
using namespace std;
int n,m,e=,head[N];
struct edge{int v,next;}ed[N<<];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
LL sum[N<<],lazy[N<<];
int dep[N],fa[N][],L[N],R[N],a[N],tot,val[N],root;
void add(int rt,int l,int r,LL x){
sum[rt]+=1ll*(r-l+)*x;
lazy[rt]+=x;
}
void pushup(int rt){
sum[rt]=sum[rt<<]+sum[rt<<|];
}
void pushdown(int rt,int l,int r){
if(lazy[rt]){
int mid=(l+r)>>;
add(rt<<,l,mid,lazy[rt]);
add(rt<<|,mid+,r,lazy[rt]);
lazy[rt]=;
}
}
void build(int rt,int l,int r){
if(l==r){sum[rt]=val[a[l]];return ;}
int mid=(l+r)>>;
build(rt<<,l,mid);
build(rt<<|,mid+,r);
pushup(rt);
}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){add(rt,l,r,z);return ;}
pushdown(rt,l,r);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
pushup(rt);
}
LL query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return sum[rt];
pushdown(rt,l,r);
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return query(rt<<,l,mid,x,y)+query(rt<<|,mid+,r,x,y);
}
void dfs(int x,int d){
dep[x]=d;
for(int i=;(<<i)<=d;i++)
fa[x][i]=fa[fa[x][i-]][i-];
L[x]=++tot;a[tot]=x;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x][])continue;
fa[v][]=x;
dfs(v,d+);
}
R[x]=tot;
}
int glca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=;~i;i--)
if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=;~i;i--)
if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][];
}
int gup(int x){
if(L[x]>=L[root]&&L[x]<=R[root])return root;
int y=root;
for(int i=;~i;i--)
if(L[x]<L[fa[y][i]]||L[x]>R[fa[y][i]])y=fa[y][i];
return fa[y][];
}
int gdown(int x){
int y=root;
for(int i=;~i;i--)
if(dep[fa[y][i]]>dep[x])y=fa[y][i];
return y;
}
int getlca(int x,int y){
int xx=gup(x),yy=gup(y);
if(xx==yy)return glca(x,y);
if(dep[xx]<dep[yy])swap(xx,yy);
return xx;
}
void update(int x,int y){
if(x==root){update(,,n,,n,y);return;}
int xx=gup(x);
if(xx==x){
update(,,n,,n,y);
xx=gdown(xx);
update(,,n,L[xx],R[xx],-y);
}
else update(,,n,L[x],R[x],y);
return ;
}
LL query(int x){
if(x==root)return query(,,n,,n);
int xx=gup(x);
if(xx==x){
xx=gdown(xx);
return query(,,n,,n)-query(,,n,L[xx],R[xx]);
}
return query(,,n,L[x],R[x]);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&val[i]);
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
}
dfs(,);
L[]=;R[]=n+;
build(,,n);
root=;
for(int i=,o,x,y,z,w;i<=m;i++){
scanf("%d",&o);
if(o==){
scanf("%d",&x);
root=x;
}
if(o==){
scanf("%d%d%d",&x,&y,&w);
z=getlca(x,y);
update(z,w);
}
if(o==){
scanf("%d",&x);
printf("%lld\n",query(x));
}
}
return ;
}
T2,离线,将询问按y排序,推式子发现是要维护一个上凸壳,这里我考试想的是搞点,但是搞直线就容易很多,一个最终的位置对应一条直线,往后走可以看成移动坐标轴,然后单调栈维护一下就可以了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <algorithm>
#define N 500500
#define LL long long
using namespace std;
int n,m,a[N],q[N],top,pp[N];
LL s[N],Ans[N];
struct query{int id,x,y;}qu[N];
bool cmpy(query a,query b){return a.y<b.y;}
LL getans(int i,int x,int y){return s[y]-s[i]+1ll*(x-y+i)*a[i];}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
s[i]=s[i-]+a[i];
}
scanf("%d",&m);
for(int i=;i<=m;i++){
scanf("%d%d",&qu[i].x,&qu[i].y);
qu[i].id=i;
}
sort(qu+,qu+m+,cmpy);
pp[]=;
for(int i=,j=,l,r,mid,fin;i<=n&&j<=m;i++){
while(top&&a[q[top]]>=a[i])top--;
while(top&&getans(i,pp[top-]+i,i)<=getans(q[top],pp[top-]+i,i))top--;
if(top)pp[top]=((s[i]-s[q[top]]-1ll*(i-q[top])*a[q[top]]-)/(a[i]-a[q[top]]))+-i;
q[++top]=i;pp[top]=-i;
for(;qu[j].y==i;j++){
l=;r=top;
while(l<=r){
mid=(l+r)>>;
if(pp[mid]+i>qu[j].x)l=mid+;
else fin=mid,r=mid-;
}
Ans[qu[j].id]=getans(q[fin],qu[j].x,qu[j].y);
}
}
for(int i=;i<=m;i++)printf("%lld\n",Ans[i]);
return ;
}
T3,倍增fft,详见mx论文。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <algorithm>
#define N 66666
#define mod 998244353
using namespace std;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int n,m,len,ny,rev[N],wn[N],tmp1[N],tmp2[N];
void ntt(int *a,int o){
register int i,j,k,t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);++j){
t=1ll*wn[o==?len/k*j:len-len/k*j]*a[i+j+(k>>)]%mod;
a[i+j+(k>>)]=(a[i+j]-t+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
if(o==-)for(i=;i<len;++i)a[i]=1ll*a[i]*ny%mod;
}
int f[][N],pp[],id[N],tot,fac[N],inv[N],pw[N],pwp[N],Ans;
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int invC(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*inv[n]*fac[m]%mod*fac[n-m]%mod;
}
int solve(int x,int y){
if(id[x+y]||x+y>n)return x+y;
int z=x+y,xx=id[x],yy=id[y];
register int i;
pp[++tot]=z;id[z]=tot;
pwp[]=;
for(i=;i<=m;++i)pwp[i]=1ll*pwp[i-]*pw[y]%mod;
for(i=;i<=m;++i){
tmp1[i]=1ll*f[xx][i]*pwp[i]%mod*fac[m-i]%mod;
tmp2[i]=1ll*f[yy][i]*invC(m,i)%mod*inv[i]%mod;
}
for(i=m+;i<len;++i)tmp1[i]=tmp2[i]=;
ntt(tmp1,);ntt(tmp2,);
for(i=;i<len;++i)tmp1[i]=1ll*tmp1[i]*tmp2[i]%mod;
ntt(tmp1,-);
for(i=;i<=m;++i)
f[tot][i]=1ll*tmp1[i]*inv[m-i]%mod;
return z;
}
void work(){
int b=n,a=,c=;
for(;b;b>>=,a=solve(a,a))
if(b&)c=solve(c,a);
}
int main(){
scanf("%d%d",&n,&m); register int i; for(len=;len<=*m;len<<=);ny=qp(len,mod-);
for(i=;i<len;++i){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
wn[]=;wn[]=qp(,(mod-)/len);
for(i=;i<=len;++i)wn[i]=1ll*wn[i-]*wn[]%mod; fac[]=;
for(i=;i<=m;++i)fac[i]=1ll*fac[i-]*i%mod;
inv[m]=qp(fac[m],mod-);
for(i=m;i>=;--i)inv[i-]=1ll*inv[i]*i%mod;
pw[]=;
for(i=;i<=m;++i)pw[i]=2ll*pw[i-]%mod; for(i=;i<=m;++i)f[][i]=C(m,i);
pp[++tot]=;id[]=tot;
work();
for(i=n;i<=m;++i)(Ans+=f[id[n]][i])%=mod;
printf("%d\n",Ans);
return ;
}
只剩19天了,思考,细节,码力。
T160分送的,100分感觉可以做,但是一直再想也没想出来,我太菜了!T280分送的,100分感觉不是什么高科技就是什么黑科技。T3回文树没想出来,写的暴力10分。
T1考虑把点拆成前后两半,查询在前面查,修改在后面修,复杂度就开了个平方。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 110000
#define int long long
#define mod 1000000007
#define pb push_back
using namespace std;
int R(int x){return 1ll*rand()*rand()%x+;}
int cnt,n,val[N];
int e=,head[N];
struct edge{int v,next;}ed[N<<];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
struct data{int p,a;}d[];
bool cmpp(data a,data b){return a.p<b.p;}
bool cmpa(data a,data b){return a.a<b.a;}
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int mul(int a,int b,int p){return (a*b-(int)(((long double)a*b+0.5)/p)*p+p)%p;}
int gcd(int x,int y){return !y?x:gcd(y,x%y);}
int qp(int a,int b,int p){
int c=;
for(;b;b>>=,a=mul(a,a,p))
if(b&)c=mul(c,a,p);
return c;
}
int prime[]={,,,,,};
bool miller_rabin(int x){
for(int i=;i<=;i++)if(x==prime[i])return ;
for(int i=;i<=;i++)if(x%prime[i]==)return ;
int y=x-,cnt=;
while(!(y&))y>>=,cnt++;
for(int i=;i<=;i++){
int now=qp(prime[i],y,x),nxt;
for(int j=;j<=cnt;j++){
nxt=mul(now,now,x);
if(nxt==&&now!=&&now!=x-)return ;
now=nxt;
}
if(now!=)return ;
}
return ;
}
int pollard_rho(int x,int y){
int c=R(x-)+,d=c,g=;
for(int i=,j=;g==;i++){
c=mul(c,c,x);
g=gcd(abs(c-d),x);
if(i==j)j<<=,d=c;
}
return g;
}
void divide(int x){
if(x==)return ;
if(miller_rabin(x)){
d[++cnt].p=x;
d[cnt].a=;
return ;
}
int now=x;
while(now==x)now=pollard_rho(x,R(x-));
divide(now);
divide(x/now);
}
int pk[N][],ths,pos,mx;
int root,tot,ch[N][],g[N],f[N];
void build(int &rt,int x){
rt=++tot;
if(x>cnt)return ;
for(int i=;i<=d[x].a;i++)
build(ch[rt][i],x+);
}
int query(int rt){
for(int i=pos+;i<=cnt;i++)
rt=ch[rt][pk[ths][i]];
return g[rt];
}
int Query(int rt,int x){
if(x==pos+)return query(rt);
int ans=;
for(int i=pk[ths][x];i<=d[x].a;i++)
UPD(ans,Query(ch[rt][i],x+));
return ans;
}
void update(int rt,int x,int o){
if(x==cnt+){
UPD(g[rt],(mod+f[ths]*o)%mod);
return ;
}
for(int i=;i<=pk[ths][x];i++)
update(ch[rt][i],x+,o);
}
void Update(int o){
int rt=root;
for(int i=;i<=pos;i++)
rt=ch[rt][pk[ths][i]];
update(rt,pos+,o);
}
void dfs(int x,int fa){
ths=x;
f[x]=x==?:Query(,);
Update();
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
}
ths=x;
Update(-);
}
signed main(){
scanf("%lld",&n);
for(int i=,u,v;i<n;i++){
scanf("%lld%lld",&u,&v);
add(u,v);add(v,u);
}
for(int i=;i<=n;i++)scanf("%lld",&val[i]);
divide(val[]);
sort(d+,d+cnt+,cmpp);
for(int i=,j=;j<=cnt;j++){
if(d[j].p==d[i].p)d[i].a++;
else d[++i]=d[j];
if(j==cnt){cnt=i;break;}
}
sort(d+,d+cnt+,cmpa);
mx=;
for(int i=;i<=cnt;i++){
int now1=,now2=;
for(int j=;j<=i;j++)now1=now1*(d[j].a+);
for(int j=i+;j<=cnt;j++)now2=now2*(d[j].a+);
if(max(now1,now2)<mx)mx=max(now1,now2),pos=i;
}
for(int i=;i<=n;i++)
for(int j=;j<=cnt;j++)
while(val[i]%d[j].p==)pk[i][j]++,val[i]/=d[j].p;
build(root,);
dfs(,);
for(int i=;i<=n;i++)printf("%lld\n",f[i]);
return ;
}
T2,正解打表,还要编码?
T3,回文自动机的slink指针,考虑每个串的贡献。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 300500
#define mod 1000000007
using namespace std;
int n,m,tot,ch[N][],fail[N],slink[N],diff[N],len[N],las[N];
int num,fa[N],L[N],R[N],mx[N<<],Ans,e=,head[N];
struct edge{int v,next;}ed[N];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
struct qwq{int l,r,id;}qu[N<<];
bool cmpr(qwq a,qwq b){return a.r<b.r;}
char s[N];
int getfail(int x,int p){
while(s[p]!=s[p-len[x]-])x=fail[x];
return x;
}
void extend(int pos,int c){
int p=getfail(las[pos-],pos);
if(!ch[p][c]){
int np=++tot;
len[np]=len[p]+;
int q=ch[getfail(fail[p],pos)][c];
fail[np]=q;
diff[np]=len[np]-len[q];
if(diff[np]==diff[q])slink[np]=slink[q];
else slink[np]=np;
add(fail[np],np);
ch[p][c]=np;
}
las[pos]=ch[p][c];
}
void dfs(int x){
L[x]=++num;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa[x])continue;
fa[v]=x;dfs(v);
}
R[x]=num;
}
void update(int rt,int l,int r,int x,int y){
if(l==r){mx[rt]=y;return ;}
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y);
else update(rt<<|,mid+,r,x,y);
mx[rt]=max(mx[rt<<],mx[rt<<|]);
}
int query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return mx[rt];
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return max(query(rt<<,l,mid,x,y),query(rt<<|,mid+,r,x,y));
}
int c[N];
void bit_add(int x,int y){for(;x<=n;x+=x&-x)c[x]+=y;}
int bit_query(int x){int y=;for(;x;x-=x&-x)y+=c[x];return y;}
int main(){
scanf("%d%d",&n,&m);
scanf("%s",s+);
tot=;
fail[]=fail[]=;len[]=-;
for(int i=;i<=n;i++)
extend(i,s[i]-'a');
add(,);
fa[]=tot+;
dfs();
for(int i=;i<=m;i++){
qu[i].id=i;
scanf("%d%d",&qu[i].l,&qu[i].r);
}
sort(qu+,qu+m+,cmpr);
for(int i=,j=,k;i<=n&&j<=m;i++){
for(k=las[i];k>&&k<=tot;k=fa[slink[k]]){
bit_add(max(query(,,tot+,L[k],R[k])-len[k]+,),);
bit_add(i-len[slink[k]]+,-);
}
for(;j<=m&&qu[j].r==i;j++)
(Ans+=1ll*qu[j].id*bit_query(qu[j].l)%mod)%=mod;
update(,,tot+,L[las[i]],i);
}
printf("%d\n",Ans);
return ;
}
18天,QAQ。
世界杯?已经结束了。
6.30
今天考试状态不是很好,明天要调整一下。
T1感觉又是个很简单的题,但是又没有想出来。T2基本上想到了正解,但是没敢打。T3时间不够了,写了50分。
T1,发现每个区间的最优转移点是固定的,可以建一个森林,然后倍增就行。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 200500
using namespace std;
int n,m,Ans,dep[N],fa[N][];
struct data{int l,r;}d[N];
bool cmpr(data a,data b){return a.r<b.r;}
int solve(int x){
int y=x;
for(int i=;~i;i--)
if(d[fa[y][i]].r<=d[x].l+m)y=fa[y][i];
return dep[x]-dep[y]+;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=;i<=n;i++){
scanf("%d%d",&d[i].l,&d[i].r);
if(d[i].r<d[i].l)d[i].r+=m;
d[i+n].l=d[i].l+m;d[i+n].r=d[i].r+m;
}
sort(d+,d+*n+,cmpr);
d[].l=d[].r=*m+;
for(int i=,j=;i<=*n;i++){
while(j<=*n&&d[j].l<d[i].r)j++;
fa[i][]=j>*n?:j;
}
for(int i=*n;i;i--){
dep[i]=dep[fa[i][]]+;
for(int j=;(<<j)<=dep[i];j++)
fa[i][j]=fa[fa[i][j-]][j-];
}
for(int i=;i<=n;i++)Ans=max(Ans,solve(i));
printf("%d\n",Ans);
return ;
}
T2,多项式,度数不同的儿子最多有根号个,复杂度就有保证了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 555555
#define mod 1000000009
#define mm 31623
#define LL long long
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int n,m,rt,mx,fac[N],inv[N],e=,head[N];
struct edge{int v,next;}ed[N];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
int size[N],in[N],tmp[N],f[N],g[N],Ans,V[N];
bool cmpsize(int a,int b){return size[a]<size[b];} const double pi=acos(-1.0);
int len,rev[N],tmp1[N],tmp2[N],tmp3[N];
struct cmx{
double x,y;
cmx(){x=y=;}
cmx(double a,double b){x=a;y=b;}
cmx operator + (const cmx & a){return cmx(x+a.x,y+a.y);}
cmx operator - (const cmx & a){return cmx(x-a.x,y-a.y);}
cmx operator * (const cmx & a){return cmx(x*a.x-y*a.y,x*a.y+y*a.x);}
cmx operator / (const double & a){return cmx(x/a,y/a);}
cmx conj(){return cmx(x,-y);}
}A[N],B[N],C[N],D[N],E[N],F[N],wn[N]; void fft(cmx *a,int o){
register int i,j,k;cmx t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);j++){
t=wn[o==?len/k*j:len-len/k*j]*a[i+j+(k>>)];
a[i+j+(k>>)]=a[i+j]-t;
a[i+j]=a[i+j]+t;
}
}
}
if(o==-)for(i=;i<len;i++)a[i]=a[i]/len;
}
#include <cstdlib>
void fft(int *a,int *b,int *c,int l1,int l2){
for(len=;len<=(l1+l2);len<<=);
for(int i=l1+;i<len;i++)a[i]=;
for(int i=l2+;i<len;i++)b[i]=;
for(int i=;i<=len;i++)wn[i]=cmx(cos(*pi/len*i),sin(*pi/len*i));
for(int i=;i<len;i++){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
for(int i=;i<len;i++){
A[i]=cmx(a[i]%mm,a[i]/mm);
B[i]=cmx(b[i]%mm,b[i]/mm);
}
fft(A,);fft(B,);
for(int i=,j;i<len;i++){
j=!i?:len-i;
C[i]=(A[i]+A[j].conj())*cmx(0.5,);
D[i]=(A[i]-A[j].conj())*cmx(,-0.5);
E[i]=(B[i]+B[j].conj())*cmx(0.5,);
F[i]=(B[i]-B[j].conj())*cmx(,-0.5);
}
for(int i=;i<len;i++){
A[i]=C[i]*E[i]+C[i]*F[i]*cmx(,);
B[i]=D[i]*E[i]+D[i]*F[i]*cmx(,);
}
fft(A,-);fft(B,-);
for(int i=,v1,v2,v3;i<len;i++){
v1=(LL)round(B[i].y)%mod;
v2=((LL)round(A[i].y)+(LL)round(B[i].x))%mod;
v3=(LL)round(A[i].x)%mod;
c[i]=((LL)v3%mod+1ll*v2*mm%mod+1ll*v1*mm%mod*mm%mod)%mod;
}
}
void cdq(int l,int r){
if(l>=r)return ;
int mid=(l+r)>>;
cdq(l,mid);cdq(mid+,r);
tmp1[]=;for(int i=l;i<=mid;i++)tmp1[i-l+]=g[i];
tmp2[]=;for(int i=mid+;i<=r;i++)tmp2[i-mid]=g[i];
if(r-l<=){
for(int i=l;i<=r;i++)g[i]=;
for(int i=;i<=mid-l+;i++)
for(int j=;j<=r-mid;j++)if(i||j)
UPD(g[l+i+j-],1ll*tmp1[i]*tmp2[j]%mod);
return ;
}
fft(tmp1,tmp2,tmp3,mid-l+,r-mid);
for(int i=l;i<=r;i++)g[i]=tmp3[i-l+];
} void dfs(int x,int fa){
size[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
size[x]+=size[v];
}
int now=,son=;g[]=;
if(x>)g[++now]=n-size[x];
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
g[++now]=size[v];
V[++son]=v;
}
cdq(,now);
sort(V+,V+son+,cmpsize);
for(int i=,j=,ans,sum=;i<=son;){
int v=V[i],sz=size[v];
while(j<=son&&size[V[j]]==sz)j++;
ans=;
for(int k=;k<=in[x];k++){
tmp[k]=g[k];
if(k)UPD(tmp[k],mod-1ll*tmp[k-]*sz%mod);
if(k<=m)UPD(ans,1ll*tmp[k]*fac[m]%mod*inv[m-k]%mod);
}
for(;i<j;i++){
UPD(Ans,1ll*ans*f[V[i]]%mod);
UPD(Ans,1ll*sum*f[V[i]]%mod);
UPD(f[x],f[V[i]]);
UPD(sum,f[V[i]]);
}
}
for(int i=;i<=in[x];i++){
tmp[i]=g[i];
if(i)UPD(tmp[i],mod-1ll*tmp[i-]*(n-size[x])%mod);
if(i<=m)UPD(f[x],1ll*tmp[i]*fac[m]%mod*inv[m-i]%mod);
}
}
namespace work1{
void Main(){
for(int i=,sum=;i<=n;i++){
UPD(Ans,1ll*sum*(+1ll*m*(n-i)%mod)%mod);
UPD(sum,(+1ll*m*(i-)%mod)%mod);
}
printf("%d\n",Ans);
}
}
int main(){
scanf("%d%d",&n,&m);
fac[]=;
for(int i=;i<=m;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[m]=qp(fac[m],mod-);
for(int i=m;i>=;i--)inv[i-]=1ll*inv[i]*i%mod;
for(int i=,u,v;i<n;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
in[u]++;in[v]++;
mx=max(mx,max(in[u],in[v]));
}
if(mx<=){work1::Main();return ;}
dfs(,);
printf("%d\n",Ans);
return ;
}
T3,打表好题,一个重要的是f[i]为f数组中i出现的次数。
#pragma GCC optimize ("O2")
#include <cstdio>
#define P 998244353
#define N 438888
int f[N],g[N],g3[N],s[N],T,n,x,ans,inv2=;
int main(){
f[]=g[]=g3[]=s[]=;
for(int i=;i<=;++i){
f[i]=f[i-f[f[i-]]]+;g[i]=g[i-]+f[i];
g3[i]=(g3[i-]+1ll*i*f[i]*(g[i]+g[i-]+)/%P)%P;
s[i]=(s[i-]+g3[i])%P;
}
scanf("%d",&T);
while(T--){
scanf("%d",&n);
int l=,r=,mid;while(l<=r){mid=(l+r)>>;if(g[mid]>=n)x=r=mid-;else l=mid+;}
ans=((long long)s[x]+g3[x]+1ll*(x+)*(n+g[x]+)%P*(n-g[x])%P*inv2%P)%P;
printf("%d\n",ans);
}
return ;
}
16天。
7.1 七月了,七月了。
感觉今天的题都比较可做。T1按根号分类就好了,T2好像相邻权值连边没有问题,T3......不会啊,想了挺久的,没想出来,QAQ。T1没清零挂了,T2零环挂了。
T1,发现一个颜色对答案作出的贡献和它出现的次数以及两侧亮的个数有关,然后根号乱搞一下。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define N 100050
#define pb push_back
using namespace std;
vector <int> V[N];
int n,nn,m,q,tot,a[N],vis[N],id[N],pp[N],pos,nei[N][],num[N],cnt[N];
bool cmpnum(int a,int b){return num[a]<num[b];}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=;i<=n;i++){
tot++;scanf("%d",&a[tot]);
if(a[tot]==a[tot-])tot--;
}
a[tot+]=;n=tot;nn=sqrt(n);
for(int i=;i<=m;i++)pp[i]=i;
for(int i=;i<=n;i++){
num[a[i]]++;
V[a[i]].pb(i);
}
sort(pp+,pp+m+,cmpnum);
pos=m;
while(num[pp[pos]]>nn)pos--;pos++;
for(int i=;i<=m;i++)id[pp[i]]=i;
for(int i=;i<=n;i++){
if(id[a[i-]]>=pos)nei[a[i]][id[a[i-]]-pos]++;
if(id[a[i+]]>=pos)nei[a[i]][id[a[i+]]-pos]++;
}
for(int i=,x,ans=,now;i<=q;i++){
scanf("%d",&x);
now=cnt[x];
for(int j=pos;j<=m;j++)if(vis[pp[j]])now+=nei[x][j-pos];
if(vis[x])ans=ans-num[x]+now;
else ans=ans+num[x]-now;
if(id[x]<pos){
for(int j=,p;j<V[x].size();j++){
p=V[x][j];
if(vis[x])cnt[a[p-]]--,cnt[a[p+]]--;
else cnt[a[p-]]++,cnt[a[p+]]++;
}
}
vis[x]^=;
printf("%d\n",ans);
}
return ;
}
T2,连边判环就行了。
#include <bits/stdc++.h>
#define N 100500
#define pb push_back
using namespace std;
vector <int> V[N];
int n,m,p[N],GO,e=,head[N],vis[N],bo[N],dep[N];
bool cmp(int a,int b){return V[a][GO]<V[b][GO];}
struct edge{int v,w,next;}ed[N];
void add(int u,int v,int w){ed[e]=(edge){v,w,head[u]};head[u]=e++;}
void dfs(int x){
vis[x]=;bo[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(!vis[v]){dep[v]=dep[x]+ed[i].w;dfs(v);}
else if(bo[v]&&(dep[x]-dep[v]+ed[i].w)){
printf("%d\n",dep[x]-dep[v]+ed[i].w);exit();}
}bo[x]=;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=m;i++){
V[i].pb();p[i]=i;
for(int j=,x;j<=n;j++){scanf("%d",&x);V[i].pb(x);}
}
for(int i=;i<=n;i++){
GO=i;sort(p+,p+m+,cmp);
for(int j=m;j>=;j--){
if(!V[p[j-]][i])break;
add(p[j],p[j-],V[p[j]][i]-V[p[j-]][i]);
}
}
for(int i=;i<=m;i++)if(!vis[i])dfs(i);
puts("-1");
}
T3,挺妙的dp,我们发现移动是可逆的,就是说我们过去了,就一定能回来,于是我们从前往后dp,f[i][j]表示前i个房间,能过来j个人,还不能多带回去人的答案。转移比较简单。优秀。想不到啊。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 1005
using namespace std;
int n,m,up,Ans,f[][N*],a[N],b[N];
void gmax(int & a,const int & b){a>b?:a=b;}
int main(){
scanf("%d%d",&n,&m);up=m;
register int i,ii,j,mx;
for(i=;i<n;i++){
scanf("%d%d",&a[i],&b[i]);
gmax(up,a[i]+b[i]);
}
for(i=;i<m;++i)f[][i]=i;
for(i=ii=,j,mx;i<n;++i,ii=-ii){
memset(f[ii],,sizeof(int)*(up+));
mx=;
for(j=;j<a[i];++j){
gmax(mx,f[ii^][j]);
gmax(f[ii][j+b[i]],f[ii^][j]+b[i]);
}
for(j=;j<b[i];++j)gmax(f[ii][j],mx+j);
for(j=a[i];j<a[i]+b[i];++j)gmax(f[ii][j-a[i]],f[ii^][j]);
for(j=a[i]+b[i];j<=up;++j)gmax(f[ii][j],f[ii^][j]);
}
for(int i=;i<=up;i++)gmax(Ans,f[ii^][i]);
printf("%d\n",Ans);
}
加油吧。
7.3
今天考试说不错也不错,说sb也sb......
T1感觉很可做,但是想了一会没想出来,先写了40分,然后开始阶乘打表,觉得70分差不多。然后去看T2,树的是分治fft?仙人掌的?有点乱,没细想。看T3,貌似O(n^2)dp很好想,写完样例不对,手玩样例也不对?!没错?什么玩意,去写T2的分治fft,一遍写对了,和暴力拍上了,然而造数据a都小于度数,没清空的错误就没拍出来,后来又去想了想T1,想出一个分解整数的算法,写了,对了。100+10+10,T2挂了,T3瞎写的过了一个subtask??
T1,分解整数dp方案数,也可以枚举1,2号点的子树大小和最大深度。p有的不是质数的,真坑爹
#pragma GCC optimize ("O3")
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 33
using namespace std;
int n,mod,Ans,f[N][N],s[N][N],C[N][N],fac[N],inv[N],g[N]={,,,,,,,,,,,,,,,,,,,,,,,,};
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){int c=;for(;b;b>>=,a=1ll*a*a%mod)if(b&)c=1ll*c*a%mod;return c;}
void dfs(int x,int dep,int last,int rest,int now1,int now2,int num){
if(!rest){UPD(f[x][dep],1ll*(now1-now2+mod)%mod*inv[num]%mod);return ;}
for(int i=min(last,rest);i;i--){
if(i!=last)dfs(x,dep,i,rest-i,1ll*C[rest][i]*now1%mod*s[i][dep-]%mod*inv[num]%mod,1ll*C[rest][i]*now2%mod*s[i][dep-]%mod*inv[num]%mod,);
else dfs(x,dep,i,rest-i,1ll*C[rest][i]*now1%mod*s[i][dep-]%mod,1ll*C[rest][i]*now2%mod*s[i][dep-]%mod,num+);
}
}
void work(int x){
for(int i=;i<=x;i++)dfs(x,i,x,x-,,,);
for(int i=;i<=;i++)s[x][i]=(s[x][i-]+f[x][i])%mod;
}
int main(){
f[][]=;
f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;
f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=;f[][]=; scanf("%d%d",&n,&mod);
if(n>){
memset(f,,sizeof f);
for(int i=;i<=;i++)
for(int j=C[i][]=;j<=i;j++)
C[i][j]=(C[i-][j-]+C[i-][j])%mod;
fac[]=;for(int i=;i<=;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[]=qp(fac[],mod-);for(int i=;i>=;i--)inv[i-]=1ll*inv[i]*i%mod; f[][]=;
for(int i=;i<=;i++)s[][i]=;
for(int i=;i<=;i++)work(i);
} for(int i=;i<=n;i++)UPD(Ans,1ll*f[n][i]%mod*i%mod);
for(int i=;i<n;i++)Ans=1ll*Ans*qp(i,mod-)%mod;
printf("%d\n%d\n",g[n]%mod,Ans); return ;
}
T2,仙人掌,不错,不错。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define pb push_back
#define N 533333
#define mod 998244353
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){int c=;for(;b;b>>=,a=1ll*a*a%mod)if(b&)c=1ll*c*a%mod;return c;}
int e=,head[N],a[N],in[N],n,m,vis[N],dad[N],dep[N];
struct edge{int v,next;}ed[N<<];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
vector<int> son[N];
int len,ny,rev[N],wn[N],tmp1[N],tmp2[N],f[N][],h[N],g[][][],pos[N];
void ntt(int *a,int o){
register int i,j,k,t;
for(i=;i<len;++i)if(i<rev[i])swap(a[i],a[rev[i]]);
for(k=;k<=len;k<<=){
for(i=;i<len;i+=k){
for(j=;j<(k>>);++j){
t=1ll*wn[o==?len/k*j:len-len/k*j]*a[i+j+(k>>)]%mod;
a[i+j+(k>>)]=(a[i+j]-t+mod)%mod;
a[i+j]=(a[i+j]+t)%mod;
}
}
}
if(o==-)for(i=;i<len;++i)a[i]=1ll*a[i]*ny%mod;
}
void init(int n){
for(len=;len<=n;len<<=);
ny=qp(len,mod-);
for(int i=;i<len;i++){
if(i&)rev[i]=(rev[i>>]>>)|(len>>);
else rev[i]=rev[i>>]>>;
}
wn[]=;wn[]=qp(,(mod-)/len);
for(int i=;i<=len;i++)wn[i]=1ll*wn[i-]*wn[]%mod;
}
int cdq(int l,int r){
if(l==r){return pos[l]-pos[l-]-;}
int mid=(l+r)>>;
int l1=cdq(l,mid),l2=cdq(mid+,r);
init(l1+l2);
for(int i=;i<=l1;i++)tmp1[i]=h[pos[l-]++i];
for(int i=l1+;i<len;i++)tmp1[i]=;
for(int i=;i<=l2;i++)tmp2[i]=h[pos[mid]++i];
for(int i=l2+;i<len;i++)tmp2[i]=;
if(len<=){
for(int i=pos[l-]+;i<=pos[l-]++l1+l2;i++)h[i]=;
for(int i=;i<=l1;i++)
for(int j=;j<=l2;j++)
UPD(h[pos[l-]++i+j],1ll*tmp1[i]*tmp2[j]%mod);
return l1+l2;
}
ntt(tmp1,);ntt(tmp2,);
for(int i=;i<len;i++)tmp1[i]=1ll*tmp1[i]*tmp2[i]%mod;
ntt(tmp1,-);
for(int i=;i<=l1+l2;i++)h[pos[l-]++i]=tmp1[i];
return l1+l2;
}
void dfs1(int x,int fa){
vis[x]=;
for(int i=head[x];i;i=ed[i].next)if(i!=fa){
int v=ed[i].v;
if(!vis[v]){dad[v]=x;dep[v]=dep[x]+;dfs1(v,i^);}
else if(dep[v]<dep[x]){son[v].pb(x);}
}
}
void dfs2(int x,int fa){
for(int i=;i<son[x].size();i++){
int v=son[x][i];
while(v!=x){vis[v]=x;v=dad[v];}
v=son[x][i];
int now=x;
while(v!=x){dfs2(v,now);now=v;v=dad[v];}
}
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==dad[x]||v==fa||vis[v]==x)continue;
dfs2(v,x);
}
int cnt=;pos[]=-;pos[]=;h[]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==dad[x]||v==fa||vis[v]==x)continue;
cnt++;pos[cnt]=pos[cnt-]+;
h[pos[cnt-]+]=f[v][];
h[pos[cnt-]+]=f[v][];
}
for(int i=;i<son[x].size();i++){
int v=son[x][i];
int ii=;
g[ii][][]=f[v][];
g[ii][][]=g[ii][][]=f[v][];
g[ii][][]=f[v][];
v=dad[v];
while(v!=x){
ii^=;
memset(g[ii],,sizeof g[ii]);
g[ii][][]=(1ll*g[ii^][][]*f[v][]%mod+1ll*g[ii^][][]*f[v][]%mod)%mod;
g[ii][][]=(1ll*g[ii^][][]*f[v][]%mod+1ll*g[ii^][][]*f[v][]%mod)%mod;
g[ii][][]=(1ll*g[ii^][][]*f[v][]%mod+1ll*g[ii^][][]*f[v][]%mod)%mod;
g[ii][][]=(1ll*g[ii^][][]*f[v][]%mod+1ll*g[ii^][][]*f[v][]%mod)%mod;
v=dad[v];
}
cnt++;pos[cnt]=pos[cnt-]+;
h[pos[cnt-]+]=g[ii][][];
h[pos[cnt-]+]=(g[ii][][]+g[ii][][])%mod;
h[pos[cnt-]+]=g[ii][][];
}
int mx=cdq(,cnt),sum=;
for(int i=;i<a[x]-&&i<=mx;i++)UPD(sum,h[i]);
f[x][]=sum;
if(mx>=a[x]-)UPD(sum,h[a[x]-]);f[x][]=sum;
if(mx>=a[x])UPD(sum,h[a[x]]);f[x][]=sum;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
add(u,v);add(v,u);
in[u]++;in[v]++;
}
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
a[i]=min(a[i],in[i]);
}
dfs1(,);
memset(vis,,sizeof vis);
dfs2(,);
printf("%d\n",f[][]);
return ;
}
T3,水。考试想到正解,md一个点也算一条有向路径???!我们考虑暴力dp,f[i][j][k]表示前i个点,黑/白点中与前面连成的交错路为奇数条的点的个数为j/k,这样转移就是枚举一下当前点的颜色,然后与前面连边就是一个(组合数的奇/偶数项的和)*(2的幂次),发现组合数的和就是2的幂次,所以每个位置转移大部分都是一样的,有区别的就是在于k/l为0时的情况。于是我们压缩状态,f[i][j][k][l]表示前i个点,是否有黑/白点与前面连成的交错路有奇数条,总交错路的方案数为奇/偶数的答案,就可以做到O(n)了。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#define mod 998244353
#define N 200500
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int n,p,Ans,c[N],pw[N],f[][][][];
int main(){
scanf("%d%d",&n,&p);
register int i,ii=;
for(i=;i<=n;++i)scanf("%d",&c[i]);
for(i=pw[]=;i<=n;++i)pw[i]=(pw[i-]<<)%mod;
f[][][][]=;
for(i=;i<n;++i,ii=-ii){
memset(f[ii^],,sizeof f[ii^]);
if(c[i+]!=){
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i-]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
}
if(c[i+]!=){
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i-]%mod);
UPD(f[ii^][][][],1ll*(f[ii][][][]+f[ii][][][])*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
UPD(f[ii^][][][],1ll*f[ii][][][]*pw[i-]%mod);
}
}
UPD(Ans,f[ii][][][p]);
UPD(Ans,f[ii][][][p]);
UPD(Ans,f[ii][][][p]);
UPD(Ans,f[ii][][][p]);
printf("%d\n",Ans);
return ;
}
13天。
7.4
先看T1,世界杯季后赛???没看懂题,又看了好久,看明白了,先秒了70分,然后觉得是组合数,找了一会规律,找出来了,拍上了。然后看T2,????这么水????,然后切了。看T3,60分白给?又找到了2的规律,然后打出来了3的规律,4,5的弃了。100+100+80=280。
T1,组合数
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 1000000007
#define N 200500
using namespace std;
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
int n,pw[N],fac[N],inv[N];
int C(int n,int m){
if(m<||m>n)return ;
if(m==||m==n)return ;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
void work(int x,int y){
if(y<x)swap(x,y);
int v1=pw[x+y+];
int v2=C(*(n--x)-(y-x),(n--x)-(y-x));
printf("%lld\n",1ll*v1*v2%mod);
}
int main(){
scanf("%d",&n);
pw[]=;
for(int i=;i<=*n;i++)pw[i]=(pw[i-]<<)%mod;
fac[]=;
for(int i=;i<=*n;i++)fac[i]=1ll*fac[i-]*i%mod;
inv[*n]=qp(fac[*n],mod-);
for(int i=*n;i>=;i--)inv[i-]=1ll*inv[i]*i%mod;
int x=,y=,w;
while(){
work(x,y);
scanf("%d",&w);
w==?y++:x++;
if(x==n||y==n)break;
}
return ;
}
T2,暴力。。
#pragma GCC optimize ("O2")
#include <cstdio>
#define mod 1000000007
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int C[][],n,m,K,ans[];
int main(){
register int i,j,o,x,y;
scanf("%d%d%d",&n,&m,&K);
for(i=;i<=n+K;++i)
for(j=C[i][]=;j<=K&&j<=i;++j)C[i][j]=(C[i-][j-]+C[i-][j])%mod;
while(m--){
scanf("%d",&o);
if(o==){
scanf("%d%d",&x,&y);
for(i=x;i<=n;++i)
UPD(ans[i],1ll*y*C[K+(i-x-)][K-]%mod);
}
else{
scanf("%d",&x);
printf("%d\n",ans[x]);
}
}
return ;
}
T3,BM找递推式。操,打表程序没了。。。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define mod 65521
#define LL long long
using namespace std;
LL n;
int K,up,len[]={,,,,,};
int c[][]={
{},
{,,},
{,,,,,,},
{,,,,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,}
};
int f[][]={
{},
{,,},
{,,,,,,},
{,,,,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,},
{,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,}
};
struct mart{
LL a[][];
mart(){memset(a,,sizeof a);}
mart operator * (const mart & B)const{
mart C;
for(int i=;i<=up;i++)
for(int j=;j<=up;j++)if(a[i][j])
for(int k=;k<=up;k++)if(B.a[j][k])
C.a[i][k]+=a[i][j]*B.a[j][k];
for(int i=;i<=up;i++)
for(int j=;j<=up;j++)C.a[i][j]%=mod;
return C;
}
}A,B;
mart qp(mart a,LL b){
mart c;
for(int i=;i<=up;i++)c.a[i][i]=;
for(;b;b>>=,a=a*a)if(b&)c=c*a;
return c;
}
int main(){
scanf("%d%lld",&K,&n);
n-=*K;up=len[K];
if(n<=up){printf("%d\n",f[K][n]);return ;}
for(int i=;i<=up;i++)A.a[][i]=f[K][up+-i];
for(int i=;i<up;i++)B.a[i][i+]=;
for(int i=;i<=up;i++)B.a[i][]=c[K][i];
A=A*qp(B,n-up);
printf("%lld\n",A.a[][]);
return ;
}
12天。
7.6
看T1,lct?二维数点啊,切了。然后看T2,只会40,发现T3是题答,考试结束了。。。。T3手玩了80分,最后写了T2的40暴力。。
T1,联通块=点数-边数。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 200500
using namespace std;
int n,m,c[N],Ans[N];
struct data{int l,r;}d[N];
bool cmp1(data a,data b){return a.r<b.r;}
struct qur{int id,l,r;}q[N];
bool cmp2(qur a,qur b){return a.r<b.r;}
void add(int x,int y){for(;x<=n;x+=x&-x)c[x]+=y;}
int query(int x){int y=;for(;x;x-=x&-x)y+=c[x];return y;}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<n;i++){
scanf("%d%d",&d[i].l,&d[i].r);
if(d[i].l>d[i].r)swap(d[i].l,d[i].r);
}
for(int i=;i<=m;i++){
q[i].id=i;
scanf("%d%d",&q[i].l,&q[i].r);
}
sort(d+,d+n,cmp1);
sort(q+,q+m+,cmp2);
for(int i=,j1=,j2=;i<=n;i++){
for(;j1<n&&d[j1].r==i;j1++)add(,),add(d[j1].l+,-);
for(;j2<=m&&q[j2].r==i;j2++)Ans[q[j2].id]=q[j2].r-q[j2].l+-query(q[j2].l);
}
for(int i=;i<=m;i++)printf("%d\n",Ans[i]);
return ;
}
T2,小dp,先排序,然后每次找最小的一个,如果另一维没有和他相同的,那么他那行/列可以直接去掉然后快速幂算贡献,否则需要简单dp一下,我写的傻了,4次的。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 22222223
#define N 105
using namespace std;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){int c=;for(;b;b>>=,a=1ll*a*a%mod)if(b&)c=1ll*c*a%mod;return c;}
int n,m,a[N],b[N],Ans=,f[][N],C[N][N],pw1[N],pw2[N];
int work(int n,int m,int x,int y,int v){
int ii=;
memset(f[ii],,sizeof f[ii]);
f[][]=;
for(int i=pw1[]=pw2[]=;i<=m+y;i++)
pw1[i]=1ll*pw1[i-]*(v+)%mod,pw2[i]=1ll*pw2[i-]*v%mod;
for(int i=;i<=n;i++){
ii^=;
memset(f[ii],,sizeof f[ii]);
for(int j=;j<=m;j++){
for(int k=;k<=j;k++){
for(int l=;l+j<=m;l++){
if(!k&&!l)UPD(f[ii][j],1ll*f[ii^][j]*(pw1[y]-pw2[y]+mod)%mod*pw2[m]%mod);
else UPD(f[ii][j+l],1ll*f[ii^][j]*C[j][k]%mod*C[m-j][l]%mod*pw1[y]%mod*pw2[m-k-l]%mod);
}
}
}
}
int v1=qp(v+,x),v2=qp(v,x),ans=;
for(int i=;i<=m;i++)UPD(ans,1ll*f[ii][i]*qp((v1-v2+mod)%mod,m-i)%mod*qp(v1,i)%mod);
return ans;
}
bool check(){
for(int i=;i<=n;i++){
int flag=;
for(int j=;j<=m;j++)if(b[j]>=a[i]){flag=;break;}
if(!flag)return ;
}
for(int i=;i<=m;i++){
int flag=;
for(int j=;j<=n;j++)if(a[j]>=b[i]){flag=;break;}
if(!flag)return ;
}
return ;
}
int main(){
for(int i=;i<=;i++)
for(int j=C[i][]=;j<=i;j++)C[i][j]=(C[i-][j-]+C[i-][j])%mod;
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)scanf("%d",&a[i]);
for(int i=;i<=m;i++)scanf("%d",&b[i]);
if(check()){puts("No solution!");return ;}
sort(a+,a+n+);sort(b+,b+m+);
for(int i1=,i2=,j1=,j2=;i1<=n&&i2<=m;i1=j1+,i2=j2+){
if(a[i1]<=b[i2]){
for(j1=i1;a[j1]==a[i1]&&j1<=n;j1++);j1--;
if(b[i2]!=a[i1])
Ans=1ll*Ans*qp((qp(a[i1]+,m-i2+)-qp(a[i1],m-i2+)+mod)%mod,j1-i1+)%mod;
else{
for(j2=i2;b[j2]==b[i2]&&j2<=m;j2++);j2--;
Ans=1ll*Ans*work(j1-i1+,j2-i2+,n-j1,m-j2,a[i1])%mod;
}
}
else{
for(j2=i2;b[j2]==b[i2]&&j2<=m;j2++);j2--;
Ans=1ll*Ans*qp((qp(b[i2]+,n-i1+)-qp(b[i2],n-i1+)+mod)%mod,j2-i2+)%mod;
}
}
printf("%d\n",Ans);
return ;
}
T3,真好玩。。。
刷刷刷!!!
7.7
T1,看起来不是很难,先写了个O(n2)的55分的dp,然后发现可以三分?写了,拍上了,觉得能过。T2高维矩阵乘?看起来只会47分,然后去看T3,裸插值75分?想了想,好像h也可以插值,过了样例,觉得没问题了。然后凉凉35+47+80=162...
T1,三啥分啊,直接单调队列啊。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define LL long long
#define N 1000500
using namespace std;
char B[<<],*SS,*TT;
#define getc ((SS==TT)&&(TT=(SS=B)+fread(B,1,1<<15,stdin),SS==TT)?0:*SS++)
int read(){
int a=;char ch=getc;
while(ch<''||ch>'')ch=getc;
while(ch>=''&&ch<=''){a=a*+(ch^);ch=getc;}
return a;
}
struct data{int t,x;}d[N],tmp[N];
inline bool cmpt(const data & a,const data & b){return a.t<b.t;}
inline void gmin(LL & a,const LL & b){a<b?:a=b;}
int n,q[N],pre[N],len[N],tot;
LL f[N];
int main(){
register int i,j,top=,he=,ta=;
n=read();
for(i=;i<=n;++i)d[i].t=read(),d[i].x=read();
sort(d+,d+n+,cmpt);
for(i=;i<=n;++i){
while(top&&d[q[top]].x<=d[i].x)--top;
pre[i]=q[top];len[i]=len[pre[i]]+;q[++top]=i;
}
tot=len[n];
for(i=tot,j=n;i;--i,j=pre[j])tmp[i]=d[j];
memset(f,0x3f,sizeof(int) * (tot+));
f[]=;
for(i=;i<=tot;++i){
while(he<=ta&&f[q[ta]]+*tmp[q[ta]+].x>f[i-]+*tmp[i].x)--ta;q[++ta]=i-;
while(he<=ta&&f[q[he]]<=tmp[i].t)++he;
f[i]=(LL)tmp[i].t+*tmp[q[he-]+].x;
if(he<=ta)gmin(f[i],f[q[he]]+*tmp[q[he]+].x);
}
printf("%lld\n",f[tot]);
return ;
}
T2,就是组合意义,然后就是等比数列求和。矩阵维护一个向量。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define mod 998244353
#define LL long long
using namespace std;
int n,K,m,T,cc[][]={{},{,},{,,},{,,,},{,,,,},{,,,,,}};
struct vec{
LL a[];
vec(){memset(a,,sizeof (LL)*(T+));}
vec operator + (const vec & B)const{
vec C;
for(int i=;i<=T;++i)C.a[i]=(a[i]+B.a[i])%mod;
return C;
}
vec operator * (const vec & B)const{
vec C;
for(int i=;i<=T;++i)if(a[i])
for(int j=;i+j<=T;++j)if(B.a[j])
C.a[i+j]+=a[i]*B.a[j]%mod*cc[i+j][i];
for(int i=;i<=T;++i)C.a[i]%=mod;
return C;
}
};
struct mart{
vec a[][];
mart operator + (const mart & B)const{
mart C;
for(int i=;i<=n;++i)
for(int j=;j<=n;++j)C.a[i][j]=a[i][j]+B.a[i][j];
return C;
}
mart operator * (const mart & B)const{
mart C;
for(int i=;i<=n;++i)
for(int j=;j<=n;++j)if(a[i][j].a[])
for(int k=;k<=n;++k)if(B.a[j][k].a[])
C.a[i][k]=C.a[i][k]+a[i][j]*B.a[j][k];
return C;
}
}A,B,C;
void solve(int x){
if(x==){
B=C=A;
return ;
}
solve(x>>);
if(x&)B=B+B*C,C=C*C*A,B=B+C;
else B=B+B*C,C=C*C;
}
int main(){
scanf("%d%d%d%d",&n,&K,&m,&T);K--;
for(int i=;i<=n;++i)
for(int j=;j<=n;++j){
scanf("%lld",&A.a[i][j].a[]);
for(int k=;k<=T;++k)
A.a[i][j].a[k]=A.a[i][j].a[k-];
}
if(K>)solve(K);
if(K>=)for(int i=;i<=n;++i)B.a[i][i].a[]++;
for(int i=,x,y;i<=m;++i){
scanf("%d%d",&x,&y);
printf("%lld\n",B.a[x][y].a[T]);
}
return ;
}
T3,插值,貌似可以只插一个点,常数能小不少。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#define int long long
#define N 1050
using namespace std;
int T,n,K,d,A,mod;
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int qp(int a,int b){int c=;for(;b;b>>=,a=a*a%mod)if(b&)c=c*a%mod;return c;}
int y[N],c[N],l[N][N],L[N],inv1[N],inv2[N];
void getfgh(){
register int i,j,ny,nn,now,ans;
for(i=;i<=K+;++i)
inv1[i]=qp(i,mod-),inv2[i]=qp(mod-i,mod-);
memset(c,,sizeof c);
c[]=;
for(i=;i<=K+;++i)
for(j=i-;~j;--j)
UPD(c[j+],c[j]),c[j]=c[j]*(mod-i)%mod; y[]=qp(,K);
for(i=;i<=K+;++i)y[i]=(y[i-]+qp(i,K))%mod;
memset(L,,sizeof L);
for(i=;i<=K+;++i){
ny=;
for(j=;j<=K+;++j)if(i!=j)ny=ny*(i>j?inv1[i-j]:inv2[j-i])%mod;
for(j=;j<=K+;++j)l[i][j]=c[j];
for(j=;j<=K+;++j){
l[i][j]=l[i][j]*inv2[i]%mod,UPD(l[i][j+],mod-l[i][j]);
l[i][j]=l[i][j]*ny%mod;
}
for(j=;j<=K+;++j)UPD(L[j],l[i][j]*y[i]%mod);
}
ans=;
for(i=,now=;i<=K+;++i,now=now*n%mod)UPD(ans,L[i]*now%mod);
printf("%lld ",ans); for(i=;i<=K+;++i)y[i]=(y[i-]+y[i])%mod;
memset(L,,sizeof L);
for(i=;i<=K+;++i)
for(j=;j<=K+;++j)UPD(L[j],l[i][j]*y[i]%mod);
ans=;
for(i=,now=;i<=K+;++i,now=now*n%mod)UPD(ans,L[i]*now%mod);
printf("%lld ",ans); for(i=,nn=A;i<=K+;++i,nn=(nn+d)%mod){
y[i]=;
for(j=,now=;j<=K+;++j,now=now*nn%mod)UPD(y[i],L[j]*now%mod);
if(i)UPD(y[i],y[i-]);
}
memset(L,,sizeof L);
for(i=;i<=K+;++i)
for(j=;j<=K+;++j)UPD(L[j],l[i][j]*y[i]%mod);
ans=;
for(i=,now=;i<=K+;++i,now=now*n%mod)UPD(ans,L[i]*now%mod);
printf("%lld\n",ans);
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld%lld%lld%lld",&K,&n,&A,&d,&mod);
getfgh();
}
}
思考。
7.9
T1网络流,没想出来。T2hall定理,真的会考啊,这么考啊,暴力没调完,T3sb后缀数组,炸int了30分。。
T1,对于黑格子分奇偶行讨论,这里如果不这样的话会出现一个流量对多个流量的问题,要多想啊。
#pragma GCC optimize ("O2")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 55
using namespace std;
int n,m,K,id[N][N],val[N][N],die[N][N],tot1,tot2;
int e=,head[*N];
struct edge{int u,v,f,w,next;}ed[*N];
void add(int u,int v,int f,int w){
ed[e]=(edge){u,v,f,w,head[u]};head[u]=e++;
ed[e]=(edge){v,u,,-w,head[v]};head[v]=e++;
}
int dis[*N],vis[*N],q[*N],pre[*N],he,ta,S,T,ss,Cost;
bool spfa(){
memset(dis,-0x3f,sizeof dis);
he=ta=;q[]=S;dis[S]=;
while(he<=ta){
int x=q[he++];vis[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]<dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;pre[v]=i;
if(!vis[v]){vis[v]=;q[++ta]=v;}
}
}
}
return dis[T]!=dis[];
}
int mcmf(){
while(spfa()&&dis[T]>)
for(int i=pre[T];i;i=pre[ed[i].u])
Cost-=ed[i].w,ed[i].f--,ed[i^].f++;
printf("%d\n",Cost);
}
int main(){
scanf("%d%d%d",&n,&m,&K);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
if((i+j)&)id[i][j]=++tot1;
else id[i][j]=++tot2;
scanf("%d",&val[i][j]);
Cost+=val[i][j];
}
for(int i=;i<=n;i++)
for(int j=;j<=n;j++)if((i+j)&)id[i][j]+=tot2;
for(int i=,x,y;i<=K;i++){
scanf("%d%d",&x,&y);
die[x][y]=;
}
S=tot2+tot1*+;T=S+;ss=T+;
add(S,ss,m,);
for(int i=;i<=n;i++){
for(int j=;j<=n;j++)if(!die[i][j]){
if((i+j)&)add(id[i][j],id[i][j]+tot1,,val[i][j]);
else{
if(i&){
add(ss,id[i][j],,);
if(i>)add(id[i][j],id[i-][j],,);
if(i<n)add(id[i][j],id[i+][j],,);
if(j>)add(id[i][j],id[i][j-],,);
if(j<n)add(id[i][j],id[i][j+],,);
}
else{
add(id[i][j],T,,);
if(i>)add(id[i-][j]+tot1,id[i][j],,);
if(i<n)add(id[i+][j]+tot1,id[i][j],,);
if(j>)add(id[i][j-]+tot1,id[i][j],,);
if(j<n)add(id[i][j+]+tot1,id[i][j],,);
}
}
}
}
mcmf();
return ;
}
T2,hall定理,一个二分图有完美匹配,那么X集中的任意k个点都要至少与Y集中的k个点相连,然后我们用线段树+bitset维护出每个人的可行颜色,然后2^c找最紧的限制就行了。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#include <bitset>
#define M 1005
#define N 300005
using namespace std;
int e=,head[N],n,m,q,cnt[],p[],c[N];
struct edge{int v,next;}ed[N];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
int dep[N],size[N],fa[N],son[N],top[N],id[N],pp[N],tot;
bitset<M> b[N<<],s[N],now[];
void dfs1(int x,int d){
dep[x]=d;size[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
dfs1(v,d+);
size[x]+=size[v];
if(size[v]>size[son[x]])son[x]=v;
}
}
void dfs2(int x,int t){
s[x][c[x]]=;
if(x!=t)s[x]|=s[fa[x]];
top[x]=t;id[x]=++tot;pp[tot]=x;
if(son[x])dfs2(son[x],t);
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==son[x])continue;
dfs2(v,v);
}
}
void build(int rt,int l,int r){
if(l==r){b[rt][c[pp[l]]]=;return ;}
int mid=(l+r)>>;
build(rt<<,l,mid);
build(rt<<|,mid+,r);
b[rt]=b[rt<<]|b[rt<<|];
}
bitset<M> query(int rt,int l,int r,int x,int y){
if(x<=l&&r<=y)return b[rt];
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return query(rt<<,l,mid,x,y)|query(rt<<|,mid+,r,x,y);
}
bitset<M> query(int x,int y){
bitset<M> ans;ans.reset();
while(top[x]!=top[y])
ans|=s[x],x=fa[top[x]];
ans|=query(,,n,id[y],id[x]);
return ans;
}
int getlca(int x,int y){
while(top[x]!=top[y])
dep[top[x]]<dep[top[y]]?y=fa[top[y]]:x=fa[top[x]];
return dep[x]<dep[y]?x:y;
}
void work(){
int num,lca,ans=0x7fffffff;
scanf("%d",&num);
for(int i=;i<=num;i++)scanf("%d",&p[i]);
lca=p[];
for(int i=;i<=num;i++)lca=getlca(lca,p[i]);
for(int i=;i<=num;i++)now[<<i-]=query(p[i],lca);
for(int i=;i<(<<num);i++){
now[i]=now[i^(i&-i)]|now[i&-i];
ans=min(ans,(int)now[i].count()/cnt[i]);
}
printf("%d\n",num*ans);
}
int main(){
for(int i=;i<=;i++)cnt[i]=cnt[i>>]+(i&);
scanf("%d%d%d",&n,&m,&q);
for(int i=;i<=n;i++){
scanf("%d",&fa[i]);
add(fa[i],i);
}
for(int i=;i<=n;i++)scanf("%d",&c[i]);
dfs1(,);dfs2(,);build(,,n);
for(int i=;i<=q;i++)work();
}
T3,sb后缀数组。。貌似可以一个log就是直接在线段树上搞,不是很清楚。
#pragma GCC optimize ("O3")
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define N 200500
using namespace std;
char s[N];
int n,tot,r[N],sa[N],rk[N],ht[N],wa[N],wb[N],buc[N],a[N],sum[N];
struct data{int l,r;data(){}data(int a,int b){l=a;r=b;}}d[N];
bool cmplr(data a,data b){return a.l==b.l?a.r<b.r:a.l<b.l;}
void getheight(int n){
register int i,j,k=;
for(i=;i<n;++i)rk[sa[i]]=i;
for(i=;i<n-;ht[rk[i++]]=k)
for(k?k--:,j=sa[rk[i]-];r[i+k]==r[j+k];k++);
return ;
}
bool cmp(int *a,int b,int c,int d){return a[b]==a[c]&&a[b+d]==a[c+d];}
void da(int n,int m=){
register int i,j,p,*x=wa,*y=wb;
for(i=;i<m;++i)buc[i]=;
for(i=;i<n;++i)buc[x[i]=r[i]]++;
for(i=;i<m;++i)buc[i]+=buc[i-];
for(i=n-;~i;--i)sa[--buc[x[i]]]=i;
for(j=,p=;p<n;j<<=,m=p){
for(p=,i=n-j;i<n;++i)y[p++]=i;
for(i=;i<n;++i)if(sa[i]>=j)y[p++]=sa[i]-j;
for(i=;i<m;++i)buc[i]=;
for(i=;i<n;++i)buc[x[y[i]]]++;
for(i=;i<m;++i)buc[i]+=buc[i-];
for(i=n-;~i;i--)sa[--buc[x[y[i]]]]=y[i];
for(swap(x,y),x[sa[]]=,p=,i=;i<n;++i)
x[sa[i]]=cmp(y,sa[i-],sa[i],j)?p-:p++;
}
getheight(n);
}
int nn[N<<],lazy[N<<];
void update(int rt,int x){nn[rt]=lazy[rt]=x;}
void pushdown(int rt){
if(lazy[rt]!=-){
update(rt<<,lazy[rt]);
update(rt<<|,lazy[rt]);
lazy[rt]=-;
}
}
void update(int rt,int l,int r,int x,int y,int z){
if(x<=l&&r<=y){update(rt,z);return ;}
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x,y,z);
if(y>mid)update(rt<<|,mid+,r,x,y,z);
}
int query(int rt,int l,int r,int x){
if(l==r)return nn[rt];
pushdown(rt);
int mid=(l+r)>>;
if(x<=mid)return query(rt<<,l,mid,x);
else return query(rt<<|,mid+,r,x);
}
long long all,f[N];
int main(){
scanf("%s",s);
n=strlen(s);
for(int i=;i<n;++i)r[i]=s[i]-'a'+;
da(n+);
for(int i=;i<n;++i){
scanf("%d",&a[i]);
sum[i]=a[i];
if(i)sum[i]+=sum[i-];
}
for(int i=;i<=n;++i)all+=n-sa[i]-ht[i];
for(int i=,x,l,r,mid,fin,pos;i<=n;++i){
x=sa[i];
update(,,n-,ht[i],n-x-,ht[i]);
f[ht[i]]=all;all-=n-x-ht[i];
l=,r=n-x-,fin=-;
while(l<=r){
mid=(l+r)>>;
pos=query(,,n-,mid);
if(sum[x+mid]-(x?sum[x-]:)<f[pos]-(mid-pos))fin=mid,l=mid+;
else r=mid-;
}fin++;
pos=query(,,n-,fin);
if((fin<n-x)&&((sum[x+fin]-(x?sum[x-]:))==f[pos]-(fin-pos)))d[++tot]=data(x,x+fin);
}
printf("%d\n",tot);
sort(d+,d+tot+,cmplr);
for(int i=;i<=tot;i++)printf("%d %d\n",d[i].l+,d[i].r+);
return ;
}
7.10
今天考试状态不是很好啊,T1本来可以做啊,懒了,每一次都要当成最后一次啊。T2的话,取模是真的没想到,不过也不是特别难想。T3的话倒是真的没什么思路,数据结构还是太弱了,这里最优解中较大的一个数一定旁边2k个没有比他大的是个很有用的小性质。多观察。
T1,可以发现,对于每一行影响的是一段,而且左右端点都单调不降,所以每一行维护一个树状数组就行了。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 2005
using namespace std;
int n,a[N][N],f[N][N],c[N][N];
long long ans;
void update(int x,int y,int z){for(;y<=n;y+=y&-y)c[x][y]+=z;}
int F(int x,int y){int z=f[x][y];for(;y;y-=y&-y)z+=c[x][y];return z;}
void upd(int x,int y){
int l=y,r=y;a[x][y]++;
for(;r<n;r++)if(F(x,r+)!=F(x,r)+a[x][r+])break;
update(x,l,);update(x,r+,-);ans+=r-l+;
for(int i=x+;i<=n;i++){
for(;l<=r;l++)if(F(i,l)==F(i-,l)-+a[i][l])break;
if(l>r)break;
for(;r<n;r++)if(F(i,r+)!=F(i,r)+a[i][r+])break;
update(i,l,);update(i,r+,-);
ans+=r-l+;
}
}
void dec(int x,int y){
int l=y,r=y;a[x][y]--;
for(;r<n;r++)if(F(x,r+)==F(x-,r+)+a[x][r+])break;
update(x,l,-);update(x,r+,);ans-=r-l+;
for(int i=x+;i<=n;i++){
for(;l<=r;l++)if(F(i,l)!=F(i,l-)+a[i][l])break;
if(l>r)break;
for(;r<n;r++)if(F(i,r+)==F(i-,r+)+a[i][r+])break;
update(i,l,-);update(i,r+,);
ans-=r-l+;
}
}
int main(){
scanf("%d",&n);
for(int i=;i<=n;i++)
for(int j=;j<=n;j++){
scanf("%d",&a[i][j]);
f[i][j]=max(f[i-][j],f[i][j-])+a[i][j];
ans+=f[i][j];
}
printf("%lld\n",ans);
char o[];
for(int i=,x,y;i<=n;i++){
scanf("%s%d%d",o,&x,&y);
if(o[]=='U')upd(x,y);
else dec(x,y);
printf("%lld\n",ans);
}
return ;
}
T2,取模乱搞。。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
int T,K,n,len,mod,flag,a[],pw[]={,,,,,,,};
char s[];
int qp(int a,int b){
int c=;
for(;b;b>>=,a=1ll*a*a%mod)
if(b&)c=1ll*c*a%mod;
return c;
}
bool check(int x){
for(int i=;i*i<=x;i++)
if(x%i==)return ;
return ;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%s%d",s+,&K);len=strlen(s+);
memset(a,,sizeof (int)*(len/+));
for(int i=;i<=len;i++)
a[(i-)/+]+=(s[len-i+]^)*pw[i%];
len=(len-)/+;flag=;
for(mod=*K+,n=;mod/K<;mod+=K,n=)if(check(mod)){
for(int i=len;i;i--)n=(100000000ll*n+a[i])%mod;
if(!n)continue;
if(qp(n,mod/K)!=){flag=;break;}
}
if(flag)puts("N");
else puts("Y");
}
return ;
}
T3,知道了上面那个性质就可以维护了,每次只需要更新两侧的最大值。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define N 1000500
#define inf 1000000000
using namespace std;
char B[<<],*SS,*TT;
#define getc (SS==TT&&(TT=(SS=B)+fread(B,1,1<<15,stdin),SS==TT)?0:*SS++)
int read(){
int a=;char ch=getc;
while(ch<''||ch>'')ch=getc;
while(ch>=''&&ch<=''){a=a*+(ch^);ch=getc;}
return a;
}
multiset <int> ms;
multiset <int> :: iterator mit;
set <int> pos;
set <int> :: iterator it;
int n,K,m,o,Ans,a[N],q[N],top,can[N],vis[N],pp[N];
int mx[N<<];
int Max(int x,int y){return a[x]>a[y]?x:y;}
void build(int rt,int l,int r){
if(l==r){mx[rt]=l;return;}
int mid=(l+r)>>;
build(rt<<,l,mid);
build(rt<<|,mid+,r);
mx[rt]=Max(mx[rt<<],mx[rt<<|]);
}
void update(int rt,int l,int r,int x){
if(l==r){mx[rt]=l;return;}
int mid=(l+r)>>;
if(x<=mid)update(rt<<,l,mid,x);
else update(rt<<|,mid+,r,x);
mx[rt]=Max(mx[rt<<],mx[rt<<|]);
}
int query(int rt,int l,int r,int x,int y){
if(x>y)return ;
if(x<=l&&r<=y)return mx[rt];
int mid=(l+r)>>;
if(y<=mid)return query(rt<<,l,mid,x,y);
if(x>mid)return query(rt<<|,mid+,r,x,y);
return Max(query(rt<<,l,mid,x,y),query(rt<<|,mid+,r,x,y));
}
void ins(int x){
if(!x)return ;
pp[x]=;
if(x>)pp[x]=Max(pp[x],query(,,n,max(,x-K),x-));
if(x<n)pp[x]=Max(pp[x],query(,,n,x+,min(n,x+K)));
if(a[pp[x]]>a[x]||(a[pp[x]]==a[x]&&pp[x]>x))return;
vis[x]=;ms.insert(a[x]+a[pp[x]]);pos.insert(x);
}
void del(int x){
vis[x]=;ms.erase(ms.find(a[x]+a[pp[x]]));pos.erase(x);
}
int main(){
n=read();K=read();m=read();o=read();
for(int i=;i<=n;i++)a[i]=read();
build(,,n);
top=;q[]=-inf;
for(int i=;i<=n;i++){
while(top&&a[i]>=a[q[top]])top--;
if(i-q[top]<=K)can[i]=-;q[++top]=i;
}
top=;q[]=inf;
for(int i=n;i;i--){
while(top&&a[i]>a[q[top]])top--;
if(q[top]-i<=K)can[i]=-;q[++top]=i;
}
pos.insert(-inf);pos.insert(inf);
for(int i=;i<=n;i++)if(can[i]!=-)ins(i);
mit=ms.end();mit--;
Ans=*mit;
printf("%d\n",Ans);
for(int i=,x,y,nx,pr;i<=m;i++){
x=read();y=read();
if(o)x^=Ans,y^=Ans;
if(vis[x]){
del(x);a[x]=y;update(,,n,x);ins(x);
ins(query(,,n,max(,x-K),x-));
ins(query(,,n,x+,min(x+K,n)));
}
else{
it=pos.upper_bound(x);
nx=*it;it--;pr=*it;
if(x-pr>K)pr=-inf;
if(nx-x>K)nx=inf;
if(pr!=-inf)del(pr);
if(nx!=inf)del(nx);
a[x]=y;update(,,n,x);ins(x);
ins(query(,,n,max(,x-K),x-));
ins(query(,,n,x+,min(x+K,n)));
if(pr!=-inf&&!vis[pr])ins(pr);
if(nx!=inf&&!vis[nx])ins(nx);
}
mit=ms.end();mit--;
Ans=*mit;
printf("%d\n",Ans);
}
return ;
}
加油吧!
7.12
T1,觉得是网络流,没想出来。写了40暴力。T2写的$n \sqrt{K}$的,只有十分??!!T3玩了前三个点,根本没想到用char好不好!!
T1,说不明白,看代码吧,挺妙的。
#pragma GCC optimize ("O3")
#include <bits/stdc++.h>
#define inf 0x7fffffff
#define N 3050
using namespace std;
map<int,int> mm[N];
map<int,int> :: iterator it,iit;
int Tim,n,m,a[N];
int e,head[N<<],dep[N<<],S,T,tot,q[N<<],he,ta;
struct edge{int u,v,f,next;}ed[N<<];
void add(int u,int v,int f){
ed[e]=(edge){u,v,f,head[u]};head[u]=e++;
ed[e]=(edge){v,u,,head[v]};head[v]=e++;
}
bool bfs(){
memset(dep,,sizeof dep);
he=ta=;q[]=S;dep[S]=;
while(he<=ta){
int x=q[he++];
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&!dep[v]){
dep[v]=dep[x]+;
if(v==T)return ;
q[++ta]=v;
}
}
}
return ;
}
int dfs(int x,int f){
if(x==T||!f)return f;
int ans=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dep[v]==dep[x]+){
int nxt=dfs(v,min(f,ed[i].f));
f-=nxt;ans+=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
if(!ans)dep[x]=-;
return ans;
}
int dinic(){
int ans=;
while(bfs())ans+=dfs(S,inf);
return ans;
}
int main(){
scanf("%d",&Tim);
while(Tim--){
scanf("%d%d",&n,&m);
e=;memset(head,,sizeof head);
S=;T=;tot=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
mm[i].clear();
}
for(int i=,x,y;i<=m;i++){
scanf("%d%d",&x,&y);
mm[x][i]=++tot;
mm[y][i]=++tot;
add(tot-,tot,);
add(tot,tot-,);
}
for(int i=;i<=n;i++){
it=mm[i].begin();
if(it!=mm[i].end()){
add(S,it->second,);
iit=it;it++;
for(;it!=mm[i].end();iit++,it++)
add(iit->second,it->second,a[i]);
if(i==)add(iit->second,T,a[i]);
}
}
printf("%d\n",dinic());
}
return ;
}
T2,留坑。
4天了。
7.13
UNR Day1 先让我冷静一下,太凉了。。。
一直在刚T1,就是不会做,然后写了T2的暴力,结论猜错了,最后T3没时间写了,凉了。
T1,我们按size排序,如果答案个数<m,那么一定是连续的一段,否则中间不选的选上一定更优,这个我们可以O(nm)做,然后我们按val从小到大枚举最小值,这时一定也是链表中与其相连的2m中的连续m个,否则可以把这个移动,答案显然不会变差,这个通过链表删除也是O(nm)的。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define LL long long
#define N 200500
using namespace std;
struct data{LL s,v;}d[N];
bool cmps(data a,data b){return a.s<b.s;}
bool cmpv(int a,int b){return d[a].v<d[b].v;}
LL sum,Ans;
int n,m,ds,dv,pp[N],pre[N],nxt[N],cnt;
LL getval(LL a,LL b){return (dv&?a:a*a)-(ds&?b:b*b);}
int main(){
scanf("%d%d%d%d",&n,&m,&ds,&dv);cnt=n;
for(int i=;i<=n;i++){
scanf("%lld%lld",&d[i].s,&d[i].v);
pp[i]=i;
}
sort(d+,d+n+,cmps);
sort(pp+,pp+n+,cmpv);
for(int i=;i<=n;i++){
sum=;
for(int j=;j<=m;j++){
if(i+j->n)break;
sum+=d[i+j-].v;
Ans=max(Ans,getval(sum,d[i+j-].s-d[i].s));
}
}
nxt[]=;pre[n+]=n;
for(int i=;i<=n;i++)pre[i]=i-,nxt[i]=i+;
for(int i=,now,x,y,j;i<=n;i++){
if(cnt<m)break;
now=pp[i];
for(j=,y=now;j<=m&&y>=;j++,y=pre[y]);y=nxt[y];
for(j=,x=y,sum=;j<=m&&x<=n;j++,sum+=d[x].v,x=nxt[x]);x=pre[x];
for(;y<=now&&x<=n;sum+=d[nxt[x]].v-d[y].v,y=nxt[y],x=nxt[x])
Ans=max(Ans,getval(sum,d[x].s-d[y].s));
cnt--;
pre[nxt[now]]=pre[now];
nxt[pre[now]]=nxt[now];
}
printf("%lld\n",Ans);
return ;
}
T2,按深度或子树size贪心。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#include <vector>
#define N 100500
#define pb push_back
using namespace std;
int n,m,Ans,fa[N],size[N],e=,head[N];
struct edge{int v,next;}ed[N];
void add(int u,int v){ed[e]=(edge){v,head[u]};head[u]=e++;}
struct data{
int x;
data(int a){x=a;}
bool operator < (const data & a)const{
return size[x]<size[a.x];
}
};
priority_queue <data> q;
vector <int> V[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++)
scanf("%d",&fa[i]);
for(int i=n;i;i--){
size[i]++,size[fa[i]]+=size[i];
add(fa[i],i);
}
q.push(data());
while(!q.empty()){
Ans++;
for(int i=;i<=m;i++){
int x=q.top().x;q.pop();
V[Ans].pb(x);
if(q.empty())break;
}
for(int i=;i<V[Ans].size();i++)
for(int j=head[V[Ans][i]];j;j=ed[j].next)
q.push(data(ed[j].v));
}
printf("%d\n",Ans);
for(int i=;i<=Ans;i++){
printf("%d ",V[i].size());
for(int j=;j<V[i].size();j++)printf("%d ",V[i][j]);
puts("");
}
return ;
}
T3,我们考虑一条边做出的贡献,我们把在其子树中的点在序列上标记为1,不在的标记为0,然后做一个前缀异或和,答案就是奇/偶数1的个数*奇/偶数0的个数,这个可以直接启发式合并或者线段树合并做。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#define mod 998244353
#define N 100500
#define pb push_back
using namespace std;
vector <int> V[N];
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int n,m,Ans,e=,head[N];
struct edge{int v,w,next;}ed[N<<];
void add(int u,int v,int w){ed[e]=(edge){v,w,head[u]};head[u]=e++;}
int sz,cnt1[N<<],cnt0[N<<],lazy[N<<],lon[N<<],ron[N<<],root[N];
void update(int &rt,int l,int r){
if(!rt)rt=++sz;
int num1=(r-l+)>>,num0=(r-l+)>>;
if(l&)swap(num1,num0);
cnt1[rt]=num1-cnt1[rt];
cnt0[rt]=num0-cnt0[rt];
lazy[rt]^=;
}
void pushdown(int rt,int l,int r){
if(lazy[rt]){
int mid=(l+r)>>;
update(lon[rt],l,mid);
update(ron[rt],mid+,r);
lazy[rt]^=;
}
}
void update(int &rt,int l,int r,int x,int y){
if(!rt)rt=++sz;
int num1=(r-l+)>>,num0=(r-l+)>>;
if(l&)swap(num1,num0);
if(x<=l&&r<=y){
update(rt,l,r);
return ;
}
pushdown(rt,l,r);
int mid=(l+r)>>;
if(x<=mid)update(lon[rt],l,mid,x,y);
if(y>mid)update(ron[rt],mid+,r,x,y);
cnt1[rt]=cnt1[lon[rt]]+cnt1[ron[rt]];
cnt0[rt]=cnt0[lon[rt]]+cnt0[ron[rt]];
}
int count(int rt){
return (1ll*cnt1[rt]*(((m+)>>)-cnt1[rt])%mod+1ll*cnt0[rt]*(((m+)>>)-cnt0[rt])%mod)%mod;
}
void dfs(int x,int fa){
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(v==fa)continue;
dfs(v,x);
UPD(Ans,1ll*ed[i].w*count(root[v])%mod);
if(V[v].size()>V[x].size()){
swap(root[x],root[v]);
swap(V[x],V[v]);
}
for(int j=;j<V[v].size();j++){
update(root[x],,m,V[v][j],m);
V[x].pb(V[v][j]);
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=,u,v,w;i<n;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);add(v,u,w);
}
for(int i=,x;i<=m;i++){
scanf("%d",&x);
update(root[x],,m,i,m);
V[x].pb(i);
}
dfs(,);
printf("%d\n",Ans);
return ;
}
加油啊!
7.14
UNR Day2 全凉了,希望给noi攒rp吧。
T1,裸跑网络流可以50,没判不联通,爆零了。T2不会,10分暴力。T3玩了4个点,最后少交了一个。
T1,把角度法变成射线法就行了。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <queue>
#define N 20010
#define inf 0x7fffffff
using namespace std;
int n,m,S,T,dex[N],in[N],e=,head[N];
int q[N],he,ta,vis[N],dis[N],Ans;
struct point{int x,y;}p[N];
struct edge{int u,v,f,w,next;}ed[N<<];
void add(int u,int v,int f,int w){
ed[e].u=u;ed[e].v=v;ed[e].f=f;ed[e].w=w;
ed[e].next=head[u];head[u]=e++;
ed[e].u=v;ed[e].v=u;ed[e].f=;ed[e].w=-w;
ed[e].next=head[v];head[v]=e++;
}
void add(int u,int v){
if(1ll*p[u].x*p[v].y<1ll*p[v].x*p[u].y)swap(u,v);
in[u]++;in[v]--;
if((p[u].y>=)==(p[v].y>=)||(p[v].y<))add(u,v,,);
else Ans++,add(u,v,,);
}
bool spfa(){
memset(dis,0x7f,sizeof(int)*(n+));
memset(vis,,sizeof(int)*(n+));
he=ta=;q[ta++]=S;dis[S]=;vis[S]=;
while(he!=ta){
int x=q[he++];if(he==N)he=;vis[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]>dis[x]+ed[i].w){
dis[v]=dis[x]+ed[i].w;
if(!vis[v]){vis[v]=;q[ta++]=v;if(ta==N)ta=;}
}
}
}
return dis[T]!=dis[];
}
int dfs(int x,int f){
if(x==T)return f;
int ans=;
vis[x]=;
for(int i=head[x];i;i=ed[i].next){
int v=ed[i].v;
if(ed[i].f&&dis[v]==dis[x]+ed[i].w&&!vis[v]){
int nxt=dfs(v,min(f,ed[i].f));
ans+=nxt,f-=nxt;ed[i].f-=nxt;ed[i^].f+=nxt;
if(!f)break;
}
}
vis[x]=;
if(!ans)dis[x]=inf;
return ans;
}
void mcmf(){
while(spfa())
Ans-=dis[T]*dfs(S,inf);
printf("%d\n",Ans);
}
int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
int main(){
scanf("%d%d",&n,&m);S=n+;T=n+;
for(int i=;i<=n;i++){
scanf("%d%d",&p[i].x,&p[i].y);
fa[i]=i;
}
for(int i=,u,v;i<=m;i++){
scanf("%d%d",&u,&v);
dex[u]++;dex[v]++;
add(u,v);
if(find(u)!=find(v))fa[find(u)]=find(v);
}
for(int i=;i<=n;i++)if(dex[i]&&find(i)!=find()){puts("-1");return ;}
for(int i=;i<=n;i++)if(in[i]&){puts("-1");return ;}
for(int i=;i<=n;i++){
if(in[i]>)add(S,i,in[i]/,);
if(in[i]<)add(i,T,-in[i]/,);
}
mcmf();
return ;
}
T2,很妙的一个dp,考虑暴力容斥一列之后至少有有多少列,然后直接变成一个背包了。
#include <cstdio>
#define N 31
#define mod 998244353
void UPD(int &a,int b){a=(a+b>=mod)?(a+b-mod):(a+b);}
int n,all,a[N],f[N][N*N],g[N][N*N],co[N][N*N],C[N*N][N*N],Ans[N];
void work(int x){
register int i,j,k;
for(i=;i<=n;++i)
for(j=;j<=all;++j)g[i][j]=f[i][j];
for(i=;i<n;++i)
for(j=;j<=all;++j)if(g[i][j])
for(k=;k<x;k++)UPD(g[i+][j+k],mod-1ll*g[i][j]*C[j+k][j]%mod);
for(i=n-;~i;--i)
for(j=;j<=all;++j)if(g[i][j])
UPD(Ans[x],1ll*g[i][j]*C[j+x-][j]%mod*co[i+][j+x]%mod*(i&?mod-:)%mod);
}
int main(){
register int i,j,k,l;
scanf("%d",&n);
for(i=;i<=n;++i)
scanf("%d",&a[i]),all+=a[i];
for(i=;i<=all;++i)
for(j=C[i][]=;j<=i;++j)C[i][j]=(C[i-][j-]+C[i-][j])%mod;
for(i=;i<=n;++i){
co[i][]=;
co[i][]=(i==?:mod-1ll*(mod/i)*co[mod%i][]%mod);
for(j=;j<=all;++j)co[i][j]=1ll*co[i][j-]*co[i][]%mod;
}
all-=n;f[][]=;
for(i=;i<=n;++i)for(j=i-;~j;--j)
for(k=;k<=all;++k)if(f[j][k])
for(l=;l<a[i];++l)UPD(f[j+][k+l],1ll*f[j][k]*C[k+l][k]%mod);
for(i=;i<=n;++i){
if(!Ans[a[i]])work(a[i]);
printf("%d ",Ans[a[i]]);
}puts("");
return ;
}
T3......这程序也能写挂???
Come on!
NOI前的考试日志的更多相关文章
- NOI前各种Idea总结以及各种文本乱堆
转载请注明原文地址:https://www.cnblogs.com/LadyLex/p/9227267.html 不过这篇的确没什么*用了转转吧 2018-6-24 关于一类延迟标记(来自UR14 思 ...
- NOI前训练日记
向别人学习一波,记点流水帐.17.5.29开坑. 5.29 早晨看了道据说是树状数组优化DP的题(hdu5542),然后脑补了一个复杂度500^3的meet in the middle.然后死T... ...
- 自动清理N天前的二进制日志
这里以自动清理5天前的二进制日志为例(做了同步或依赖于二进制日志备份的请慎用): 以root身份登录数据库,执行以下命令: ; 首次设置expire_logs_days参数后需要执行flush log ...
- [日常] NOI前划水日记
NOI前划水日记 开坑记录一下每天的效率有多低 5.24 早上被春哥安排了一场NEERC(不过怎么是qualification round啊) 省队势力都跑去参加THU/PKU夏令营了...剩下四个D ...
- Python2.7 删除前N天日志文件
Python2.7 删除前N天日志文件 import os import sys import time day_n = 7 path=os.getcwd().replace("\\&quo ...
- 使用XML文件记录操作日志,并从后往前读取操作日志并在richTextBox1控件中显示出来
#region 获取本地程序操作记录日志 /// <summary> /// 获取本地程序更新日志信息(由后往前读取) /// </summary> private void ...
- [总结] NOIP 前的考试记录
sb博主又犯sb错误了! 他觉得以往模拟赛因为犯sb错误扔的分足足有1k分了! 于是他想记录一下自己犯的sb错误看看自己到底有多sb! 嗯就从今天开始吧 2018.9.28 1. 二分边界写错.骚什么 ...
- 这是C语言结课前(期末考试之前)写给牛晓霞的一封信!
致尊敬的牛晓霞老师: 这是黄领衫的感想,也是想告诉你的话! 在老师说要给班里写得好的人发黄领衫的时候,我当时的想法是我很有可能拿到这份奖品的,怎么说呢,算是一种自信吧,或是对自己的态度的认可.虽然我能 ...
- python 打包前三天日志
日志格式 app-2019-07-24.log app-2019-07-24.1.log 该脚本适合一天之内有多个日志文件 # /usr/bin/python #-*- coding: utf-8 - ...
随机推荐
- CSDN的博客搜索功能不又给力了呵呵呵呵
不得不说,CSDN博客的搜索功能是在太弱了.而且一直都很弱,以至于我每次想在自己博客上找自己发的文章都变得那么难.做一个搜索博客内文章的功能没有那么难吧? 还是说CSDN已经放弃了博客这一块了? 我发 ...
- 我的sql数据库存储过程分页- -
以前用到数据库存储过程分页的时候都是用 not in 但是最近工作的时候,随着数据库记录的不断增大,发现not in的效率 真的不行 虽然都设置了索引,但是当记录达到10w的时候就发现不行了,都是需要 ...
- Lintcode395 Coins in a Line II solution 题解
[题目描述] There are n coins with different value in a line. Two players take turns to take one or two c ...
- docker的安装和基础使用
Docker EE/Docker CE简介与版本规划 版本区别 Docker EE Docker EE由公司支持,可在经过认证的操作系统和云提供商中使用,并可运行来自Docker Store的.经过认 ...
- spring cloud 入门系列五:使用Feign 实现声明式服务调用
一.Spring Cloud Feign概念引入通过前面的随笔,我们了解如何通过Spring Cloud ribbon进行负责均衡,如何通过Spring Cloud Hystrix进行服务断路保护,两 ...
- 实验6 shell程序设计一(1)
设计如下一个菜单驱动程序 Use one of the following options: P:To display current directory S:To display the name ...
- 0基础一分钟入门Python
这篇文章面向所有想学python的小伙伴(甚至你从没听过编程),这篇文章将会带你以最快的速度入门python.赶快上车,时间来不及了... 一,下载和安装python 1.下载: 1.1 python ...
- nltk download失败
之前在台式机win10的系统,python 2.7,用的pycharm执行nltk download(),很顺利.然而到了我的笔记本只是换个一个win8的系统,Python的配置都是一样的,但是这时候 ...
- angular2 安装 打包成发布项目过程
安装之前要有typings和typescript全局已经安装好 安装命令新版为npm install -g @angular/cli 原来的angular-cli为老版的,我安装失败了 安装之后新建一 ...
- 基于ASP.NET MVC 微信网页登录授权(scope为snsapi_base) 流程 上 获取OPENID
流程图 我们需要判断是否存在OPENID 首先我们得先定义一个全局的OPENID 类似于普通账号密码登录系统的 当前登录用户ID 因为我是MVC 框架 我这里定义一个控制器基类 BaseCont ...