开学了住校了打不了深夜场node
好难受啊QwQios
显然对于每一个起点,咱们只须要贪心记录这个起点出发出去的糖果数量以及离本身最近的糖果git
由于这个起点最后一次装载糖果必定是装载终点离本身最近的那个糖果数组
$ O(n^2)$暴力贪心便可ui
有线性作法懒得写了spa
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; int sum[20010],far[20010]; int main(){ n=read();m=read(); for(rt i=1;i<=m;i++){ x=read();y=read(); sum[x]++; if((y+n-x)%n<far[x]||sum[x]==1)far[x]=(y+n-x)%n; } for(rt i=1;i<=n;i++){ int ans=0; for(rt j=1;j<=n;j++)ans=max(ans,(sum[j]-1)*n+far[j]+(j+n-i)%n); write(ans),putchar(' '); } return 0; }
构造题的作法不少样化code
一开始写了一个乱七八糟的乱搞过了blog
而后看了眼标程提供的Answer顿感智商被碾压排序
咱们只须要在最前面放一个$ -1$get
后面放一段长度为$ len$,和为$ sum$的非负整数序列便可
则差值为$$(sum-1)(len+1)-sum·len=sum-len-1$$
随便拿个$ len$跑出来一组$sum$便可
我原先作法就不讲了
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; void pt(int x,int y){ for(rt i=1;i<=y;i++)if(x<=1000000)cout<<x<<' ',x=0; else cout<<1000000<<' ',x-=1000000; } int main(){ k=read(); int S=k+1927,L=1926; cout<<L+1<<endl; cout<<-1<<' ';pt(S,L); return 0; }
每次的答案至关于上次的答案加上当前串全部后缀的贡献
那每次把当前串反向插入到$ trie$树中
若是有新建节点就利用这个点往上四层的四个祖先计算$ DP$值并加入答案
注意细节
个人$ trie$树奇丑无比不要模仿....
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 1000000007 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans,Root; struct trie{ int son[2],fa; }a[9000010]; int v[3010],dp[9000010]; void insert(int &x,int pl){ if(!x)x=++cnt;if(pl==0)return; bool fla=0;int val=0; if(!a[x].son[v[pl]])a[x].son[v[pl]]=++cnt;a[a[x].son[v[pl]]].fa=x; if(!dp[a[x].son[v[pl]]]){ int now=x,zhi=0; for(rt i=0;i<4;i++){ if(!now)continue; zhi|=(1<<i)*(v[pl+i]); if((zhi==12||zhi==10||zhi==7||zhi==15)&&i==3)break; (val+=dp[now])%=p;now=a[now].fa; } dp[a[x].son[v[pl]]]=val,(ans+=val)%=p; } insert(a[x].son[v[pl]],pl-1); } int main(){ n=read();dp[1]=1; for(rt i=1;i<=n;i++){ v[i]=read();z=i; insert(Root,i); writeln(ans); } return 0; }
考虑构造一个新数组v
每当新在末尾增长一个数$a_i$的时候,把$ v_i$设置成1,把上一个值和$ a_i$相同的位置的v改为-1,把上上个值和$ a_i$相同的位置的v改为0
这么作的意义是$ \sum\limits_{j=L}^i v_j$刚好表示了这个后缀中只出现一次的数的数量
则有$ DP$方程:$$ dp_i=\sum_{j=0}^{i-1}dp_j[\sum_{d=j+1}^i v_d \leq k]$$
设$ S_i$表示$ \sum\limits_{j=1}^i v_j$
则有$ DP$方程:$$ dp_i=\sum_{j=1}^{i-1}dp_j(S_j\geq S_i-k)$$
每次求$ dp_i$的时候$ S_i$都可当作一个常数
所以每次至关于求全部$ S_j$不超过某个值的$ dp_j$之和
考虑分块
$ f_{x,y}$表示第$ x$个块,全部$ S$值不超过$ y$的$ dp$值之和
每次对$ S$修改的时候整块的在上面打标记,非整块暴力重构便可
时空复杂度均为$ O(n \sqrt{n})$
代码中为了方便将数组从$ 0$开始编号,与上面略有不一样,请稍加注意
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #define p 998244353 #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; int la[100010],now[100010],v[100010],s[100010],dp[100010]; int f[400][100010],tag[350],blo,maxs[350],id[100010]; void build(int x){ int Min=1000000000,Max=-1000000000; for(rt i=x*blo;i<(x+1)*blo;i++){ if(s[i]<Min)Min=s[i]; if(s[i]>Max)Max=s[i]; } maxs[x]=Max-Min; for(rt i=0;i<=maxs[x];i++)f[x][i]=0; tag[x]+=Min; for(rt i=x*blo;i<(x+1)*blo;i++)s[i]-=Min,(f[x][s[i]]+=dp[i])%=p; for(rt i=1;i<=Max-Min;i++)(f[x][i]+=f[x][i-1])%=p; } void change(int L,int val,int R){ if(val==v[L])return; int upd=val-v[L];v[L]=val; if(id[L]==id[R]){ for(rt i=L;i<=R;i++)s[i]+=upd; return; } for(rt i=L;i<(id[L]+1)*blo;i++)s[i]+=upd;build(id[L]); for(rt i=id[L]+1;;i++){ if(i==id[R]){ for(rt j=i*blo;j<=R;j++)s[j]+=upd; return; } tag[i]+=upd; } } int query(int x,int val){ //块x中大于等于val的值 val-=tag[x]; if(val>maxs[x])return 0; if(val<=0)return f[x][maxs[x]]; return (f[x][maxs[x]]-f[x][val-1])%p; } int main(){ n=read();k=read();blo=(int)sqrt(n); for(rt i=0;i<n;i++)id[i]=i/blo; for(rt i=1;i<=n;i++)now[i]=la[i]=-1; for(rt i=0;i<n;i++){ x=read();if(i)s[i]=s[i-1]+tag[(i-1)/blo]; if(now[x]!=-1)change(now[x],-1,i); if(la[x]!=-1)change(la[x],0,i); la[x]=now[x];now[x]=i;v[i]=1;s[i]++; for(rt j=0;j<=i;j+=blo)(dp[i]+=query(id[j],s[i]+tag[id[i]]-k))%=p; for(rt j=i/blo*blo;j<i;j++)if(s[j]+tag[id[j]]>=s[i]+tag[id[i]]-k)(dp[i]+=dp[j])%=p; if(s[i]<=k)dp[i]++; if((i+1)%blo==0)build(id[i]); } cout<<(dp[n-1]%p+p)%p; return 0; }
颇有趣的构造题
钦定一号点为根
首先用$ n-1$次询问得出每一个点的$ size$大小
显然$ size$小的点不可能成为$ size$大的点的父亲
按$ size$排序,并按$ size$从小到大判断每一个点的孩子编号
思路是将当前全部$ size$比本身小且不是任何点的孩子的节点排成一行
用一个集合维护全部不是任何点的孩子的节点
每次二分找出最靠左的那一个是本身孩子的节点,并将其从集合中删除
最后把本身加入集合便可
复杂度是每一个点的孩子数·$\log$即$ O(n·\log n)$的
#include<ctime> #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> #include<vector> #include<set> #define flush fflush(stdout) #define rt register int #define ll long long using namespace std; inline ll read(){ ll x=0;char zf=1;char ch=getchar(); while(ch!='-'&&!isdigit(ch))ch=getchar(); if(ch=='-')zf=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-'0',ch=getchar();return x*zf; } void write(ll y){if(y<0)putchar('-'),y=-y;if(y>9)write(y/10);putchar(y%10+48);} void writeln(const ll y){write(y);putchar('\n');} int k,m,n,x,y,z,cnt,ans; int size[505]; struct node{ int x,size; bool operator <(const node s)const{ return size<s.size; } }a[505]; int q[505],t; set<int>s; int erase[505],fa[505],top; bool chk(int L,int R,int nd){ writeln(1);writeln(1); writeln(R-L+1); for(rt i=L;i<=R;i++)write(q[i]),putchar(' ');putchar('\n'); writeln(nd);flush; return read(); } int main(){ n=read();size[1]=n; for(rt i=2;i<=n;i++){ writeln(1); writeln(1); writeln(n-1); for(rt j=2;j<=n;j++)write(j),putchar(' ');putchar('\n'); writeln(i);flush; size[i]=read(); } for(rt i=1;i<=n;i++)a[i]={i,size[i]}; sort(a+1,a+n+1); for(rt i=1;i<=n;i++){ t=0; for(auto i:s)q[++t]=i; for(rt lef=1;lef<=t;){ if(!chk(lef,t,a[i].x))break; int L=lef,R=t,ans=0; while(L<=R){ const int mid=L+R>>1; if(chk(L,mid,a[i].x))ans=mid,R=mid-1; else L=mid+1; } lef=ans+1;fa[q[ans]]=a[i].x;erase[++top]=q[ans]; } for(rt i=top;i>=1;i--)s.erase(erase[i]);top=0; s.insert(a[i].x); } puts("ANSWER"); for(rt i=2;i<=n;i++)write(fa[i]),putchar(' '),writeln(i);flush; return 0; }