bzoj 4386
把一个点拆成三个点跑矩乘快速幂,因为要求一个前缀和所以新建一个点,连一个自环,把所有点的第三个点连过去。
因为可能暴long long,所以我用的long double,然后很慢。。。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ld long double
int n,m;
ll K,ans;
int num[52][8],cnt;
struct matrix
{
ld w[150][150];
friend matrix operator * (const matrix &r1,const matrix &r2)
{
matrix ret;
memset(&ret,0,sizeof(ret));
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
for(int k=1;k<=cnt;k++)
ret.w[i][j]+=r1.w[i][k]*r2.w[k][j];
return ret;
};
}a[110],b,tmp;
ld cal(matrix &x)
{
ld ret=0;
for(int i=1;i<=n;i++)
ret+=x.w[num[i][3]][cnt];
return ret-n;
}
int main()
{
scanf("%d%d%lld",&n,&m,&K);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=3;j++)
num[i][j]=++cnt;
a[0].w[num[i][1]][num[i][2]]++;
a[0].w[num[i][2]][num[i][3]]++;
}
cnt++;
a[0].w[cnt][cnt]++;
for(int i=1;i<=n;i++)
a[0].w[num[i][3]][cnt]++;
for(int i=1,x,y,z;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
a[0].w[num[x][3]][num[y][4-z]]++;
}
int lim;
for(lim=1;lim<=61;lim++)
{
a[lim]=a[lim-1]*a[lim-1];
if(cal(a[lim])>K)break;
}
for(int i=1;i<=cnt;i++)b.w[i][i]=1;
for(;lim>=0;lim--)
{
tmp=b*a[lim];
if(cal(tmp)<K)
b=tmp,ans+=1ll<<lim;
}
if(ans>(1ll<<61))puts("-1");
else printf("%lld\n",ans);
return 0;
}
bzoj 4385
最近好zz呀,妈蛋这个单调队列竟然没想出来。。。。啊,怎么办啊怎么办。。。
单调队列,枚举区间的右端点,然后左端点单调,删除区间也单调,用单调队列维护删除区间就行了。。。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define N 2100000
int n,d;
ll p,a[N],sum[N];
int q[N],h,r,ans;
ll cal(int x){return sum[x]-sum[x-d];}
char getc()
{
static const int LEN = 4096;
static char buf[LEN],*S=buf,*T=buf;
if(S == T)
{
T = (S=buf)+fread(buf,1,LEN,stdin);
if(S == T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0'; isdigit(ch=getc());)
D=(D<<3)+(D<<1)+(ch-'0');
return D;
}
int main()
{
scanf("%d%lld%d",&n,&p,&d);
for(int i=1;i<=n;i++)
a[i]=read(),sum[i]=sum[i-1]+a[i];
h=1;r=0;
for(int i=d,j=1;i<=n;i++)
{
while(h<=r&&cal(q[r])<=cal(i))r--;
q[++r]=i;
while(sum[i]-sum[j-1]-cal(q[h])>p)
{
j++;
if(h<=r&&q[h]-d+1<j)h++;
}
ans=max(ans,i-j+1);
}
printf("%d\n",ans);
return 0;
}
bzoj 4384
树状数组。
对于只有一个数的情况扫一遍就行了。
将一个位置表示为三个前缀和两两做差的三个数。如果
i,j
满足
i,j
的三个数彼此不同,那么可以用
abs(i−j)
更新答案。
第一维排序解决,按第二维存两个树状数组分别是正序和倒序,然后查询时在两个里分别查第二维大的和小的。对于第三维维护位置最大值,次大值,位置取最大值时第三维的值,最小值,次小值,位置取最小值时第三维的值。
注意次大值,次小值第三维的值必须和最大值,最小值的第三维值不同。
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n,lim,ans;
char s[N];
struct node
{
int a,b,c,pos;
node(){}
node(int a,int b,int c,int pos):a(a),b(b),c(c),pos(pos){}
friend bool operator < (const node &r1,const node &r2)
{return r1.a<r2.a;};
}p[N];
struct diw_tree
{
int mx[N<<1],sx[N<<1],cx[N<<1],mn[N<<1],sn[N<<1],cn[N<<1];
void init()
{
memset(mx,-0x3f,sizeof(mx));
memset(sx,-0x3f,sizeof(sx));
memset(mn,0x3f,sizeof(mn));
memset(sn,0x3f,sizeof(sn));
}
void insert(int x,int y,int v)
{
for(int i=x;i<=lim;i+=i&-i)
{
if(v>mx[i])
{
if(cx[i]!=y)sx[i]=mx[i];
mx[i]=v;cx[i]=y;
}
else if(y!=cx[i])
sx[i]=max(sx[i],v);
if(v<mn[i])
{
if(cn[i]!=y)sn[i]=mn[i];
mn[i]=v;cn[i]=y;
}
else if(y!=cn[i])
sn[i]=min(sn[i],v);
}
}
void query(int x,int y,int v)
{
for(int i=x;i;i-=i&-i)
{
if(cx[i]!=y)ans=max(ans,mx[i]-v);
else ans=max(ans,sx[i]-v);
if(cn[i]!=y)ans=max(ans,v-mn[i]);
else ans=max(ans,v-sn[i]);
}
}
}tr1,tr2;
int main()
{
scanf("%d",&n);lim=(n+1)<<1;
scanf("%s",s+1);
for(int i=1,j;i<=n;i++)
{
j=i;
while(s[j]==s[i]&&j<=n)j++;j--;
ans=max(ans,j-i+1);i=j;
}
for(int i=1,a=0,b=0,c=0;i<=n;i++)
{
if(s[i]=='B')a++;
if(s[i]=='C')b++;
if(s[i]=='S')c++;
p[i]=node(a-b,b-c,c-a,i);
}
tr1.init();tr2.init();
sort(p,p+1+n);
for(int i=0,j;i<=n;i++)
{
j=i;
while(p[j].a==p[i].a&&j<=n)j++;j--;
for(int k=i;k<=j;k++)
{
tr1.query(p[k].b-1+n+1,p[k].c,p[k].pos);
tr2.query(n-(p[k].b+1)+1,p[k].c,p[k].pos);
}
for(int k=i;k<=j;k++)
{
tr1.insert(p[k].b+n+1,p[k].c,p[k].pos);
tr2.insert(n-p[k].b+1,p[k].c,p[k].pos);
}
i=j;
}
printf("%d\n",ans);
return 0;
}
bzoj 4382
要是一个颜色最多有两个怎么做?
对于出现两次的颜色,在第一次出现时+1,最后一次出现时-1。然后两个点能成为分割点当且仅当两个点的前缀和相同。然后把前缀和相同的点放在一起,跑一个单调队列,就可以求出第二问了。
然后如果一个颜色出现多次的话为了防止重复hash一下每个点加的值就行了。
因为用了map跑的巨慢。。
#include <bits/stdc++.h>
using namespace std;
#define seed 233333
#define N 1100000
#define ll long long
#define ull unsigned long long
int n,K,ans,tot,top;
ll cnt;
ull pw[N],val[N],sum[N];
int pre[N],a[N],st[N],nex[N],vis[N];
map<ull,int>ma;
int cal(int x,int y)
{
int t=y-x;
return abs(t-(n-t));
}
int main()
{
//freopen("tt.in","r",stdin);
scanf("%d%d",&n,&K);
pw[0]=1;
for(int i=1;i<=n;i++)pw[i]=pw[i-1]*seed;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(pre[a[i]])
{
val[pre[a[i]]]+=pw[++tot];
val[i]-=pw[tot];
}
pre[a[i]]=i;
}
for(int i=1;i<=n;i++)
sum[i]=sum[i-1]+val[i];
for(int i=1;i<=n;i++)
{
if(ma.count(sum[i]))
nex[ma[sum[i]]]=i;
ma[sum[i]]=i;
}
ans=n;
for(int now=1;now<=n;now++)
if(!vis[now])
{
top=0;
for(int i=now;i;i=nex[i])
vis[i]=1,st[++top]=i;
cnt+=(ll)top*(top-1)/2;
for(int i=1,j=1;i<=top;i++)
{
while(j<i&&cal(st[j],st[i])>cal(st[j+1],st[i]))j++;
ans=min(ans,cal(st[j],st[i]));
}
}
printf("%lld %d\n",cnt,ans);
return 0;
}
bzoj 4381
维护
p[i][j]
表示点i向根走j步走到的点,
sum[i][j]
表示点i每次向根走j步经过的所有点的权值和。这两个第二维处理到
n−−√
。再维护一个树剖。
然后求的时候如果一步大小小于
n−−√
,那么直接用
sum
求一个区间和就行了。否则在树剖上一步一步走。
总时间复杂度
O(nn−−√)
#include <bits/stdc++.h>
using namespace std;
#define N 51000
#define ll long long
int n,tot,cnt,lim;
int a[N],b[N],head[N],nex[N<<1],to[N<<1];
int deep[N],fa[N],size[N],pos[N],bel[N],top[N],son[N];
int p[N][241],sum[N][241];
void add(int x,int y)
{
tot++;
nex[tot]=head[x];head[x]=tot;
to[tot]=y;
}
void dfs1(int x,int y)
{
fa[x]=y;size[x]=1;
deep[x]=deep[y]+1;p[x][1]=y;
for(int i=2;i<=lim;i++)p[x][i]=fa[p[x][i-1]];
for(int i=1;i<=lim;i++)sum[x][i]=sum[p[x][i]][i]+a[x];
for(int i=head[x];i;i=nex[i])
if(to[i]!=y)
{
dfs1(to[i],x),size[x]+=size[to[i]];
son[x]=size[to[i]]>size[son[x]] ? to[i]:son[x];
}
}
void dfs2(int x,int y,int tp)
{
bel[pos[x]=++cnt]=x;top[x]=tp;
if(son[x])dfs2(son[x],x,tp);
for(int i=head[x];i;i=nex[i])
if(to[i]!=y&&to[i]!=son[x])
dfs2(to[i],x,to[i]);
}
int lca(int x,int y)
{
while(top[x]!=top[y])
{
if(deep[top[x]]<deep[top[y]])swap(x,y);
x=fa[top[x]];
}
return deep[x]<deep[y] ? x:y;
}
int up(int x,int y)
{
while(deep[x]-deep[top[x]]<y)
{
y-=deep[x]-deep[top[x]]+1;
x=fa[top[x]];
}
return bel[pos[x]-y];
}
int get(int x,int y,int v)
{
if(v<lim)return sum[x][v]-sum[y][v]+a[y];
int ret=0,rem=0,t;
while(top[x]!=top[y])
{
for(t=pos[x]-rem;t>=pos[top[x]];t-=v)
ret+=a[bel[t]];
t+=v;rem=v-(t-pos[top[x]]+1);
x=fa[top[x]];
}
for(t=pos[x]-rem;t>=pos[y];t-=v)
ret+=a[bel[t]];
return ret;
}
int cal(int x,int y,int v)
{
int z=lca(x,y),ret=0;
int t=deep[x]-deep[z]-(deep[x]-deep[z])%v;
t=up(x,t);
ret+=get(x,t,v);
if(y==z)return t==y ? ret:ret+a[y];
int t1=v-(deep[t]-deep[z]),t2=deep[y]-deep[z]-t1;
if(t2<0)return ret+a[y];
int t3=up(y,t2%v),t4=up(y,t2);
ret+=get(t3,t4,v);
return t2%v ? ret+a[y]:ret;
}
int main()
{
scanf("%d",&n);
lim=sqrt(n)+1;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0);dfs2(1,0,1);
for(int i=1;i<=n;i++)scanf("%d",&b[i]);
for(int i=1,x;i<n;i++)
{
scanf("%d",&x);
printf("%d\n",cal(b[i],b[i+1],x));
}
return 0;
}
bzoj 4380
最终每一个点的值一定为某个ci,将ci离散化。
区间dp,设f[i][j][k]表示当区间 [ i , j ] 中的最小值为k时的最大收益。
转移:f[i][j][k]=max(f[i][x-1][q1]+f[x+1][j][q2]+cnt*k)(q1,q2>=k)
val为左端点在 [i,x] 且右端点在 [x,i] 且值大于等于k的区间个数。
时间复杂度O(n^3*m)。
#include <bits/stdc++.h>
using namespace std;
#define N 52
#define M 4010
#define ll long long
int n,m,cnt,top;
int l[M],r[M],c[M],a[M],pos[N][N][M],val[N][N][M];
ll f[N][N][M],sum[N][N][M],st[M];
void print(int l,int r,int v)
{
if(l>r)return;
int t=val[l][r][v];
print(l,pos[l][r][v]-1,t);
printf("%d ",a[t]);
print(pos[l][r][v]+1,r,t);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&l[i],&r[i],&c[i]),a[i]=c[i];
cnt=m;sort(a+1,a+1+cnt);
cnt=unique(a+1,a+1+cnt)-a-1;
for(int i=0;i<n;i++)
for(int j=1;j+i<=n;j++)
{
for(int k=j;k<=j+i;k++)
{
top=0;
for(int t=1;t<=m;t++)
if(l[t]>=j&&r[t]<=j+i&&l[t]<=k&&r[t]>=k)
st[++top]=c[t];
sort(st+1,st+1+top);
for(int t=1,now=1;t<=cnt;t++)
{
while(now<=top&&st[now]<a[t])now++;
ll t1=sum[j][k-1][t]+sum[k+1][j+i][t]+(ll)a[t]*(top-now+1);
if(t1>=f[j][j+i][t])
{
f[j][j+i][t]=t1;
pos[j][j+i][t]=k;
}
}
}
for(int k=cnt;k>=1;k--)
{
val[j][j+i][k]=k;
if(f[j][j+i][k]<sum[j][j+i][k+1])
{
pos[j][j+i][k]=pos[j][j+i][k+1];
val[j][j+i][k]=val[j][j+i][k+1];
}
sum[j][j+i][k]=max(sum[j][j+i][k+1],f[j][j+i][k]);
}
}
printf("%lld\n",sum[1][n][1]);
print(1,n,1);
return 0;
}
bzoj 4379
考虑假如已经知道删哪条边如何求加一条边后最大和最小的直径。
删完后图被分成两块,设两块的直径分别为
l1,l2
。
那么最大直径是
l1+l2+1
最小直径是
max(l1,l2,l1+12+l2+12+1)
然后按深搜顺序遍历这个树,求删掉父亲边的结果。
删边后子树那一块的直径dp算一下就行了。
剩下那块的直径求起来有点麻烦,看代码吧。。。。
#include <bits/stdc++.h>
using namespace std;
#define N 510000
int n,tot;
int head[N],nex[N<<1],to[N<<1];
int px[N],py[N],f[N],p[N],val[N],deep[N],mid[N];
int fa[N][21];
struct node
{
int x1,y1,x2,y2,v;
void print()
{printf("%d %d %d %d %d\n",v,x1,y1,x2,y2);}
}a1,a2;
struct poi
{
int x,y,v,pos;
poi(){}
poi(int x,int y,int v,int pos):x(x),y(y),v(v),pos(pos){}
friend bool operator < (const poi &r1,const poi &r2)
{return r1.v<r2.v;};
friend bool operator > (const poi &r1,const poi &r2)
{return r1.v>r2.v;};
friend poi operator + (const poi &r1,const poi &r2)
{return poi(r1.x,r2.x,r1.v+r2.v,0);};
}pre[N];
int cmp(poi x,poi y){return x>y;}
vector<poi>v1[N],v2[N];
char getc()
{
static const int LEN = 4096;
static char buf[LEN],*S=buf,*T=buf;
if(S == T)
{
T = (S=buf)+fread(buf,1,LEN,stdin);
if(S == T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0'; isdigit(ch=getc());)
D=(D<<3)+(D<<1)+(ch-'0');
return D;
}
void add(int x,int y)
{
tot++;
nex[tot]=head[x];head[x]=tot;
to[tot]=y;
}
int up(int x,int y)
{
for(int i=0;i<=20;i++)
if(y>>i&1)x=fa[x][i];
return x;
}
void dfs(int x,int y)
{
p[x]=x;deep[x]=deep[y]+1;
px[x]=x;py[x]=x;
fa[x][0]=y;
for(int i=1;i<=20;i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i;i=nex[i])
if(to[i]!=y)
{
dfs(to[i],x);
if(val[to[i]]>val[x])
{
val[x]=val[to[i]];
px[x]=px[to[i]];py[x]=py[to[i]];
}
if(f[x]+f[to[i]]+1>val[x])
{
val[x]=f[x]+f[to[i]]+1;
px[x]=p[x];py[x]=p[to[i]];
}
if(f[to[i]]+1>f[x])
{
f[x]=f[to[i]]+1;
p[x]=p[to[i]];
}
}
if(deep[px[x]]<deep[py[x]])
swap(px[x],py[x]);
mid[x]=up(px[x],val[x]/2);
}
void dfs1(int x,int y,poi p1)
{
if(y)
{
int t=val[x]+p1.v+1;
if(t>a2.v)a2=(node){x,y,p1.x,px[x],t};
t=max(val[x],p1.v);
t=max(t,(val[x]+1)/2+(p1.v+1)/2+1);
if(t<a1.v)a1=(node){x,y,mid[x],mid[y],t};
}
int sum=0;
for(int i=head[x],u;i;i=nex[i])
if((u=to[i])!=y)
{
v1[x].push_back(poi(px[u],py[u],val[u],u));
v2[x].push_back(poi(p[u],0,f[u]+1,u));
sum++;
}
sort(v1[x].begin(),v1[x].end(),cmp);
sort(v2[x].begin(),v2[x].end(),cmp);
for(int i=head[x],u;i;i=nex[i])
if((u=to[i])!=y)
{
poi t=p1;
pre[u]=pre[x];
if(pre[x].v>t.v)t=poi(pre[x].x,x,pre[x].v,0);
if(v2[x][0].pos!=u)
pre[u]=max(pre[u],v2[x][0]),t=max(t,v2[x][0]+pre[x]);
else if(sum>1)
pre[u]=max(pre[u],v2[x][1]),t=max(t,v2[x][1]+pre[x]);
pre[u].v++;
if(v1[x][0].pos!=u)t=max(t,v1[x][0]);
else if(sum>1)t=max(t,v1[x][1]);
if(sum>=2)
{
if(v2[x][0].pos==u)
{
if(sum>2)t=max(t,v2[x][1]+v2[x][2]);
}
else if(v2[x][1].pos==u)
{
if(sum>2)t=max(t,v2[x][0]+v2[x][2]);
}
else t=max(t,v2[x][0]+v2[x][1]);
}
if(deep[t.x]<deep[t.y])swap(t.x,t.y);
mid[x]=up(t.x,t.v/2);
dfs1(u,x,t);
}
}
int main()
{
//freopen("tt.in","r",stdin);
n=read();
for(int i=1,x,y;i<n;i++)
{
x=read();y=read();
add(x,y);add(y,x);
}
dfs(1,0);
a1.v=n+1;val[1]=0;px[1]=py[1]=1;
mid[1]=1;
dfs1(1,0,poi(1,1,0,0));
a1.print();a2.print();
return 0;
}
bzoj 4378
设序列中
≥s
的数的个数为
x
。
如果
否则如果序列中
因此离散化一下,然后用树状数组维护一下区间个数,区间和就可以了。
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
#define ll long long
#define PA pair<ll,int>
int n,m;
ll sum[N];
int cnt[N],a[N],st[N],top;
char s[11];
struct node
{
int x,y,type;
node(){}
node(int x,int y,int type):x(x),y(y),type(type){}
}p[N];
void insert(int x,int v)
{
for(int i=x;i<=top;i+=i&-i)
{
sum[i]+=(ll)st[x]*v;
cnt[i]+=v;
}
}
PA query(int x)
{
PA ret(0,0);
for(int i=x;i;i-=i&-i)
{
ret.first+=sum[i];
ret.second+=cnt[i];
}
return ret;
}
int main()
{
//freopen("tt.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%s%d%d",s,&p[i].x,&p[i].y);
p[i].type=(s[0]=='U');
if(s[0]=='U')st[++top]=p[i].y;
}
for(int i=1;i<=n;i++)a[i]=1;
st[++top]=0;
sort(st+1,st+1+top);
top=unique(st+1,st+1+top)-st-1;
insert(1,n);
for(int i=1;i<=m;i++)
{
if(p[i].type)
{
p[i].y=lower_bound(st+1,st+1+top,p[i].y)-st;
insert(a[p[i].x],-1);a[p[i].x]=p[i].y;
insert(p[i].y,1);
}
else
{
int t=p[i].y;
p[i].y=lower_bound(st+1,st+1+top,p[i].y)-st;
PA t1=query(top),t2=query(p[i].y-1);
int num=t1.second-t2.second;
if(num>=p[i].x)puts("TAK");
else if(t2.first>=(ll)(p[i].x-num)*t)puts("TAK");
else puts("NIE");
}
}
return 0;
}
bzoj 4377
由于n和a互质,因此
[0,n−1]
的每个数恰好出现一次。
设短串的第1个位置在长串中匹配位置为
i1
,
x=a∗i1+b
如果短串的第i个数为0那么
(x+a∗i)%n
不在区间
[p,n−1]
中。
否则
(x+a∗i)%n
不在区间
[0,p−1]
中。
并且
i1
不在区间
[n−m+1,n−1]
中。既
x≠a(n−m+1)b....(n−1)b
然后求一下这些区间并的补集就是答案。
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n,a,b,p,m,cnt,ans;
char s[N];
struct node
{
int l,r;
node(){}
node(int l,int r):l(l),r(r){}
friend bool operator < (const node &r1,const node &r2)
{return r1.l<r2.l;};
}d[N<<2];
void insert(int l,int r)
{
if(l<=r)d[++cnt]=node(l,r);
else d[++cnt]=node(l,n-1),d[++cnt]=node(0,r);
}
int main()
{
scanf("%d%d%d%d%d",&n,&a,&b,&p,&m);
scanf("%s",s);
for(int i=0,now=0;i<m;i++,now=(now+a)%n)
{
if(s[i]=='0')insert((p-now+n)%n,(n-1-now+n)%n);
if(s[i]=='1')insert((n-now)%n,(p-1-now+n)%n);
}
for(int i=1,c=(b-a+n)%n;i<m;i++,c=(c-a+n)%n)
insert(c,c);
sort(d+1,d+1+cnt);int en=-1;
for(int i=1;i<=cnt;i++)
{
if(d[i].l>en)ans+=d[i].l-en-1,en=d[i].r;
else en=max(en,d[i].r);
}
printf("%d\n",ans+n-1-en);
return 0;
}
bzoj 3750
记录印章中有墨水的点,按顺序枚举纸上有墨水的点,然后模拟印一下就行了。第一次竟然用了set。。。
#include <bits/stdc++.h>
using namespace std;
#define N 1100
int T;
int n,m,a,b,cnt;
bool used[N][N];
struct node
{
int x,y;
node(){}
node(int x,int y):x(x),y(y){}
}p[N*N];
char s1[N][N],s2[N][N];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%d",&n,&m,&a,&b);
for(int i=1;i<=n;i++)
scanf("%s",s1[i]+1);
cnt=0;
for(int i=1;i<=a;i++)
{
scanf("%s",s2[i]+1);
for(int j=1;j<=b;j++)
if(s2[i][j]=='x')p[++cnt]=node(i,j);
}
int flag=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(s1[i][j]=='x')
{
for(int k=1;k<=cnt;k++)
{
node t1=node(i+p[k].x-p[1].x,j+p[k].y-p[1].y);
if(t1.x>n||t1.y>m){flag=1;break;}
if(s1[t1.x][t1.y]!='x'){flag=1;break;}
s1[t1.x][t1.y]='.';
}
}
if(flag)break;
}
if(flag)break;
}
puts(flag ? "NIE":"TAK");
}
return 0;
}
bzoj 3749
首先一个人是否满意和他左右以及他的取法有关。
因此可以用一个0到7的整数状压这个状态。对于第i个人0表示取第i个食物,1表示取第i+1个食物。
然后枚举第一个人的状态,设
f[i][j]
表示到第i个人取状态j是否可行。然后枚举时考虑和上一个人的状态是否匹配。
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
int n;
bool v[N][8],f[N][8];
int a[N],st[N],trs[8][11],cnt[8];
short pre[N][8];
int get(int x)
{
if(x==n+1)return 1;
if(x==0)return n;
return x;
}
char getc()
{
static const int LEN = 4096;
static char buf[LEN],*S=buf,*T=buf;
if(S == T)
{
T = (S=buf)+fread(buf,1,LEN,stdin);
if(S == T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0'; isdigit(ch=getc());)
D=(D<<3)+(D<<1)+(ch-'0');
return D;
}
void print(int x)
{
if(!x)return;
print(x/10);
putchar(x%10+'0');
}
int main()
{
n=read();
for(int i=1;i<=n;i++)a[i]=read();
a[0]=a[n];a[n+1]=a[1];
for(int i=1;i<=n;i++)
for(int j=0;j<8;j++)
{
int x=(j&4) ? a[i]:a[i]<<1;
int y=(j&1) ? a[i+1]<<1:a[i+1];
if(j&2)v[i][j]=(y>=x);
else v[i][j]=(y<=x);
}
int flag=0;
for(int i=0;i<8;i++)
for(int j=0;j<8;j++)
if((i>>1)==(j&3))trs[i][++cnt[i]]=j;
for(int now=0;now<8;now++)
if(v[1][now])
{
memset(f,0,sizeof(f));
f[1][now]=1;
for(int i=2;i<=n;i++)
for(int j=0,t;j<8;j++)
if(v[i][j])
for(int k=1;k<=cnt[j];k++)
if(f[i-1][t=trs[j][k]])
f[i][j]=1,pre[i][j]=t;
for(int i=0;i<8;i++)
if(f[n][i]&&(now>>1)==(i&3))
{
for(int j=n,t=i;j>=1;j--)
{
st[j]=t&2 ? get(j+1):get(j);
t=pre[j][t];
}
for(int j=1;j<=n;j++)
print(st[j]),putchar(' ');
flag=1;break;
}
if(flag)break;
}
if(!flag)puts("NIE");
return 0;
}
bzoj 3748
这个东西1000以内打表,大于1000的时候打表可知超重数的规律为先出现一个数,然后加上一个二次函数(差分后是一个每次加2的一次函数)得到下一个数。然后依次加上16,4,12,…1,1,3,1,出现下一个数,然后再加上那个二次函数,以此类推。
因为每次加上二次函数,因此这个东西是三次的。
1018
以内依次枚举就可以。前面0ms的应该是求出了通项,Orz
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int pre=817;
ll v3=14,v1=70,v2=29;
int f[1100];
int a[35]={16,4,12,4,16,4,5,7,12,1,3,1,10,1,1,3,1,3,1,1,3,1,3,3,1,3,1,1,3,1,};
ll ans,n;
void init()
{
for(int i=1;i<=1000;i++)
{
f[i]=1000;
for(int j=1;j*j<=i;j++)
if(f[i-j*j]<j){f[i]=j;break;}
}
for(int i=1;i<=min(n,(ll)pre);i++)
{
int flag=0;
for(int j=1;j<=50;j++)
if(f[i+j]<f[i])flag=1;
ans+=flag;
}
}
int main()
{
scanf("%lld",&n);
init();
if(n<=pre)
{
if(f[n]==1000)printf("-");
else printf("%d",f[n]);
printf(" %lld\n",ans);return 0;
}
for(ll i=pre;i<=n;)
{
if(i+v1>n)
{
if(n-i<=2)v3--;
printf("%lld %lld\n",v3,ans);return 0;
}
i+=v1;
if(i+126>n)
{
ans++;
for(int j=0;j<30;j++)
{
if(i+a[j]>n)break;
i+=a[j];ans++;
}
if(i==n)v3++;
printf("%lld %lld\n",v3,ans);
break;
}
v1+=v2;v2+=2;v3++;
i+=126;ans+=31;
}
return 0;
}
bzoj 3747
我草,这题为什么这么多细节。。。。。
感觉写这题的心情堪比当年插头dp。。。。
顺便%一下鏼爷,虽然并不知道他写了什么。。。
算了不扯淡了。。
首先p=0,1时直接特判。
p=2时只有两种情况,模拟一下。
然后p=3,考虑从1到n往环里插点。那么插第i个点只与第i-1,i-2,i-3个点的位置有关。因为这个点不能和其他点相邻。并且一定是原来两个相邻,然后插到中间。
注意每次转移会踢掉一个点。对于那个不能在右边的条件,只需要考虑踢掉的这个点的左右都是谁就行了。
然后对于插到1和n之间的这种情况,只需要记录一个01状态表示i-1,i-2,i-3中两侧的点是否处于1,n就可以了。因为如果不处于1,n那么永远不会转移到这种情况。
设
f[i][j][k][t]
表示现在处理到点i,三个点位置关系为j(共6种,我用的四进制标号),相邻情况为k(两对4种),两侧的点是否处于1,n。
然后最后答案要除n。
#include <bits/stdc++.h>
using namespace std;
#define N 1100000
#define ll long long
#define mod 1000000007
#define g(x,y) ((x)>>2*(y)&3)
int n,m,p1,ans;
int a[7]={6,9,18,24,33,36,};
int b[7][3]={{0,1,2},{0,2,1},{1,0,2},{1,2,0},{2,0,1},{2,1,0}};
int trs[6][4]={{4,1,4,0},{5,3,5,2},{1,1,4,0},{1,0,4,0},{3,3,5,2},{3,2,5,2}};
int trs1[6][4][4],pos[N];
int f[2][6][4][2],p[3];
bool v[N][7];
int qpow(int x,int y)
{
int ret=1;
while(y)
{
if(y&1)ret=(ll)ret*x%mod;
x=(ll)x*x%mod;y>>=1;
}
return ret;
}
int check()
{
pos[n+1]=pos[1];
for(int i=1;i<=n;i++)
if(v[pos[i]][pos[i]-pos[i+1]+3])
return 0;
return 1;
}
void solve2()
{
pos[1]=n;
int i,j,k;
for(i=n,j=2,k=1;i>j;i--,j++,k++)
pos[i]=n-k*2+1,pos[j]=n-k*2;
i++;j--;
if(i-j==2)pos[i-1]=1;
ans+=check();
for(i=n,j=2,k=1;i>j;i--,j++,k++)
pos[i]=n-k*2,pos[j]=n-k*2+1;
i++;j--;
if(i-j==2)pos[i-1]=1;
ans+=check();
printf("%d\n",ans);
}
void solve3()
{
for(int i=0,t;i<6;i++)
f[1][i][3][1]=1;
for(int i=4;i<=n;i++)
{
memset(f[i&1],0,sizeof(f[i&1]));
for(int j=0;j<6;j++)
{
p[0]=b[j][0];p[1]=b[j][1];p[2]=b[j][2];
for(int k=1;k<4;k++)
for(int t=0;t<2;t++)
if(k>>t&1)
{
int l=0,r=0;
if(t)
{
if(!p[2])l=i;
if(!p[1])r=i;
if(k&1)
{
if(!p[0])r=p[1]+i-3;
if(!p[1])l=p[0]+i-3;
}
}
else
{
if(!p[0])r=i;
if(!p[1])l=i;
if(k&2)
{
if(!p[1])r=p[2]+i-3;
if(!p[2])l=p[1]+i-3;
}
}
if(r&&v[i-3][i-r])continue;
if(l&&v[l][l-i+6])continue;
(f[i&1][trs[j][t]][trs1[j][k][t]][0]+=f[~i&1][j][k][0])%=mod;
if(!p[0]){l=p[2]+i-3;if(v[l][l-i+6])continue;}
if(!p[2]){r=p[0]+i-3;if(v[i-3][i-r])continue;}
if(p[0]&&p[2])(f[i&1][trs[j][t]][trs1[j][k][t]][1]+=f[~i&1][j][k][1])%=mod;
else (f[i&1][trs[j][t]][trs1[j][k][t]][0]+=f[~i&1][j][k][1])%=mod;
}
for(int k=0;k<4;k++)
{
int l=0,r=0;
if(k&1)
{
if(!p[0])r=p[1]+i-3;
if(!p[1])l=p[0]+i-3;
}
if(k&2)
{
if(!p[1])r=p[2]+i-3;
if(!p[2])l=p[1]+i-3;
}
if(!p[0])l=i;
if(!p[2])r=i;
if(r&&v[i-3][i-r])continue;
if(l&&v[l][l-i+6])continue;
if(p[2])(f[i&1][trs[j][2]][trs1[j][k][2]][1]+=f[~i&1][j][k][1])%=mod;
else (f[i&1][trs[j][2]][trs1[j][k][2]][0]+=f[~i&1][j][k][1])%=mod;
if(p[0])(f[i&1][trs[j][3]][trs1[j][k][3]][1]+=f[~i&1][j][k][1])%=mod;
else (f[i&1][trs[j][3]][trs1[j][k][3]][0]+=f[~i&1][j][k][1])%=mod;
}
}
}
for(int i=0;i<6;i++)
{
p[0]=n-2+b[i][0];p[1]=n-2+b[i][1];p[2]=n-2+b[i][2];
for(int j=0;j<4;j++)
{
if((j&1)&&v[p[0]][p[0]-p[1]+3])continue;
if((j&2)&&v[p[1]][p[1]-p[2]+3])continue;
for(int k=0;k<2;k++)
{
if(k&&v[p[2]][p[2]-p[0]+3])continue;
(ans+=f[n&1][i][j][k])%=mod;
}
}
}
ans=(ll)ans*qpow(n,mod-2)%mod;
printf("%d\n",ans);
}
char getc()
{
static const int LEN = 4096;
static char buf[LEN],*S=buf,*T=buf;
if(S == T)
{
T = (S=buf)+fread(buf,1,LEN,stdin);
if(S == T)return EOF;
}
return *S++;
}
int read()
{
static char ch;
static int D;
while(!isdigit(ch=getc()));
for(D=ch-'0'; isdigit(ch=getc());)
D=(D<<3)+(D<<1)+(ch-'0');
return D;
}
int main()
{
n=read();m=read();p1=read();
if(n==1)return puts("1"),0;
if(p1==0)return puts("0"),0;
if(n==2)return puts(m ? "0":"1"),0;
if(p1==1)return puts("0"),0;
for(int i=1,x,y;i<=m;i++)
{
x=read();y=read();
if(abs(x-y)<=3)v[x][x-y+3]=1;
}
if(p1==2)
{solve2();return 0;}
for(int i=0;i<6;i++)
{
if(!b[i][1])trs1[i][3][0]=1,trs1[i][3][1]=2;
else trs1[i][3][0]=trs1[i][3][1]=3;
if(!b[i][2])trs1[i][1][0]=3;
else trs1[i][1][0]=1;
if(!b[i][0])trs1[i][2][1]=3;
else trs1[i][2][1]=2;
for(int j=0;j<4;j++)
{
int cnt=0,t1;
if(b[i][0]&&b[i][1]&&j&1)cnt++;
if(b[i][1]&&b[i][2]&&j&2)cnt++;
t1=cnt<<1;if(b[i][0])t1++;
trs1[i][j][2]=t1;
t1=cnt;if(b[i][2])t1=t1+2;
trs1[i][j][3]=t1;
}
}
solve3();
return 0;
}
总结:感觉POI的题并不是按难度顺序排列的,并不知道官网上分的类有什么卵用。。。不过题还算不错(虽然脑洞)。
最后祝大家身体健康。