開學了住校了打不了深夜場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; }