目錄html
比賽連接ios
VP了一場最近的沒作過的ACM。ACM的Online Mirror好少啊...
B題看不懂樣例=-= K題簽到題。其它題咕咕了。git
先要想到令\(f[i][j]\)表示模\(d\)餘數爲\(i\),數位和爲\(j\)的最小數。狀態數是OK的。
而後每次轉移就是在後面加上\(0\sim 9\)這些數字,用BFS轉移就能夠保證當前數最小了。複雜度\(O(10ds)\)。
固然還有其它奇奇怪怪的DP方法。數組
//124ms 32100KB #include <queue> #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=503,M=5003; bool vis[N][M]; struct Node { int r,sum; }; struct Path { int r,sum; char c; }pre[N][M]; void BFS(const int d,const int s) { std::queue<Node> q; q.push((Node){0,0}), vis[0][0]=1; while(!q.empty()) { Node x=q.front(); q.pop(); if(!x.r && x.sum==s) return; for(int i=0; i<10; ++i) { int r=(x.r*10+i)%d, sum=x.sum+i; if(sum<=s && !vis[r][sum]) pre[r][sum]=(Path){x.r,x.sum,char(i+48)}, vis[r][sum]=1, q.push((Node){r,sum}); } } } void Output(int d,int s) { if(pre[d][s].c) Output(pre[d][s].r,pre[d][s].sum), pc(pre[d][s].c); } int main() { int d,s; scanf("%d%d",&d,&s); BFS(d,s); if(vis[0][s]) Output(0,s); else puts("-1"); return 0; }
我寫的比較無腦... 按天數爲下標建線段樹。將物品區間按價格排序,考慮依次加到線段樹對應區間上。
設當前區間剩餘所需個數的最小值是\(mn\)(初始都爲\(k\)),當前物品區間、個數、單位價格分別是\((l,r,a,cost)\)。\(mn\gt a\)時,直接區間減\(a\)統計一下答案就好了;當\(mn\leq a\)時,必定至少有一個位置會被減成\(0\),暴力在線段樹找到那個/那些位置把\(size\)清零,\(mn\)設爲\(INF\),統計一下答案便可。
由於每一個數只會被清空一次,也就是隻會暴力作\(O(n)\)次,因此複雜度\(O((n+m)\log n)\)。
其實只要拿價格建一棵值域線段樹,每一個位置維護有多少個物品就好了。查詢就是在樹上二分出前\(k\)小的和(樹狀數組也行)。網絡
//327ms 78600KB #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() #define MAXIN 500000 //#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++) typedef long long LL; const int N=1e6+5; const LL INF=3e18; char IN[MAXIN],*SS=IN,*TT=IN; struct Node { int l,r,num,cost; bool operator <(const Node &x)const { return cost<x.cost; } }A[N]; struct Segment_Tree { #define ls rt<<1 #define rs rt<<1|1 #define lson l,m,ls #define rson m+1,r,rs #define S N<<2 LL mn[S];//mn[rt]=INF要足夠大! int sz[S],tag[S]; LL Ans; #undef S #define Upd(rt,v) mn[rt]+=v, tag[rt]+=v #define Update(rt) mn[rt]=std::min(mn[ls],mn[rs]), sz[rt]=sz[ls]+sz[rs] inline void PushDown(int rt) { Upd(ls,tag[rt]), Upd(rs,tag[rt]), tag[rt]=0; } void Build(int l,int r,int rt,int K) { mn[rt]=K, sz[rt]=r-l+1; if(l==r) return; int m=l+r>>1; Build(lson,K), Build(rson,K); } void Modify2(int l,int r,int rt,int a,int cost) { if(mn[rt]>a) {Ans+=1ll*cost*a*sz[rt], Upd(rt,-a); return;} if(l==r) {Ans+=1ll*cost*mn[rt], sz[rt]=0, mn[rt]=INF; return;} if(tag[rt]) PushDown(rt); int m=l+r>>1; Modify2(lson,a,cost), Modify2(rson,a,cost), Update(rt); } void Modify(int l,int r,int rt,int L,int R,int a,int cost) { if(L<=l && r<=R) {Modify2(l,r,rt,a,cost); return;} if(tag[rt]) PushDown(rt); int m=l+r>>1; if(L<=m) Modify(lson,L,R,a,cost); if(m<R) Modify(rson,L,R,a,cost); Update(rt); } }T; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now; } int main() { #define S 1,n,1 const int n=read(),K=read(),m=read(); for(int i=1; i<=m; ++i) A[i]=(Node){read(),read(),read(),read()}; std::sort(A+1,A+1+m), T.Build(S,K); for(int i=1; i<=m; ++i) T.Modify(S,A[i].l,A[i].r,A[i].num,A[i].cost); printf("%I64d\n",T.Ans); return 0; }
模擬一下就好了。
由於剛開始讀錯題還寫了個二分=v=ui
#include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=2e5+5; int A[N]; inline int read() { int now=0,f=1;register char c=gc(); for(;!isdigit(c);c=='-'&&(f=-1),c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now*f; } bool Check(LL x,int n,int K) { LL las=0; for(int i=1; i<=n; ++i) { LL need=(las+K-1)/K; las=std::max(0ll,A[i]-(need*K-las)); x-=need; if(x<0) return 0; } return x>=(las+K-1)/K;//不要寫x*K!!! } signed main() { int n=read(),K=read(); LL sum=0; for(int i=1; i<=n; ++i) sum+=A[i]=read(); LL l=0,r=3e18,mid,ans=r; while(l<=r) if(Check(mid=l+r>>1,n,K)) ans=mid, r=mid-1; else l=mid+1; printf("%I64d\n",ans); return 0; }
顯然答案是能夠三分的。可是答案是整數三分有點麻煩。
考慮能不能二分一個答案\(mid\)是否可行。若是至少選\(mid\)個,那麼要選的確定是最便宜的\(mid\)個物品。那麼模擬一下就好了。spa
//46ms 2200KB(離散化的版本,那個寫的太醜了留這個代碼叭) #include <cstdio> #include <cctype> #include <algorithm> #define gc() getchar() typedef long long LL; const int N=2e5+5; int A[N],ref[N]; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now; } inline LL readll() { LL now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now; } bool Check(int x,int n,int m,LL T) { LL sum=0;//longlongaaaTAT for(int res=0,now=0,i=1,val=ref[x]; i<=n; ++i) if(A[i]<=val) { if((T-=A[i])<0) return 0; if(++res>=x) return 1; sum+=A[i]; if(++now==m) now=0, T-=sum, sum=0; } return 0; } int main() { ref[0]=1; for(int Ts=read(); Ts--; ) { const int n=read(),m=read(); const LL t=readll(); for(int i=1; i<=n; ++i) ref[i]=A[i]=read(); std::sort(ref+1,ref+1+n); int l=1,r=n,mid,ans=0; while(l<=r) if(Check(mid=l+r>>1,n,m,t)) ans=mid, l=mid+1; else r=mid-1; printf("%d %d\n",ans,ref[ans]); } return 0; }
記四種人分別是\(1,2,3,4\)。顯然\(2,3\)能夠同時選,而後選一個\(4\)能夠多選一個\(1/2+3\)。排序從大到小選就好了。code
//46ms 6100KB #include <cstdio> #include <cctype> #include <cstring> #include <algorithm> #include <functional> #define pc putchar #define gc() getchar() typedef long long LL; const int N=4e5+5; int val[4][N]; inline int read() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now; } inline int read01() { int now=0;register char c=gc(); for(;!isdigit(c);c=gc()); for(;isdigit(c);now=now*2+c-48,c=gc()); return now; } int main() { const int n=read(); int cnt[4]={0}; for(int i=1,type; i<=n; ++i) type=read01(), val[type][++cnt[type]]=read(); if(!cnt[3]&&(!cnt[1]||!cnt[2])) return puts("0"),0; for(int i=0; i<4; ++i) std::sort(val[i]+1,val[i]+cnt[i]+1,std::greater<int>()); int p0=1,p1=1,p2=1,p3=1,ans=0; for(; p1<=cnt[1]&&p2<=cnt[2]; ans+=val[1][p1++]+val[2][p2++]); for(int i=1; i<=cnt[3]; ++i) { if(val[1][p1]+val[2][p2]>val[0][p0]) ans+=val[1][p1++]+val[2][p2++]; else ans+=val[0][p0++]; ans+=val[3][i]; } printf("%d\n",ans); return 0; }
怎麼寫AC自動機、寫Hash的都有... 這不是BZOJ2780嗎?
粘過代碼來就完了。然而我粘錯了WA了兩次。htm
//77ms 17500KB #include <cstdio> #include <cctype> #include <string> #include <cstring> #include <iostream> #include <algorithm> #define pc putchar #define gc() getchar() typedef long long LL; const int N=10005*10; using namespace std; int mp[233]; string str[10005]; struct Suffix_Automaton { int tot,las,fa[N],len[N],son[N][40],cnt[N],bef[N]; void Insert(int c,int now) { int np=++tot,p=las; len[las=np]=len[p]+1; for(; p&&!son[p][c]; p=fa[p]) son[p][c]=np; if(!p) fa[np]=1; else { int q=son[p][c]; if(len[q]==len[p]+1) fa[np]=q; else { int nq=++tot; len[nq]=len[p]+1, bef[nq]=bef[q], cnt[nq]=cnt[q]; memcpy(son[nq],son[q],sizeof son[q]); fa[nq]=fa[q], fa[q]=fa[np]=nq; for(; son[p][c]==q; p=fa[p]) son[p][c]=nq; } } for(; bef[np]!=now&&np; np=fa[np]) ++cnt[np], bef[np]=now; } void Query(char *s,int l) { int x=1; for(int i=0; i<l; ++i) x=son[x][mp[s[i]]]; cout << cnt[x] << ' ' << str[bef[x]] << '\n'; } }sam; inline int read() { int now=0,f=1;register char c=gc(); for(;!isdigit(c);c=='-'&&(f=-1),c=gc()); for(;isdigit(c);now=now*10+c-48,c=gc()); return now*f; } int main() { static char s[233]; int cnt=0; mp['.']=cnt++; for(int i='0'; i<='9'; ++i) mp[i]=cnt++; for(int i='a'; i<='z'; ++i) mp[i]=cnt++; int n=read(); sam.tot=1; for(int i=1; i<=n; ++i) { scanf("%s",s), str[i]=s; sam.las=1; for(int j=0,l=strlen(s); j<l; ++j) sam.Insert(mp[s[j]],i); } str[0]="-"; for(int Q=read(); Q--; scanf("%s",s), sam.Query(s,strlen(s))); return 0; }
\(Description\)
給定一張\(n\)個點\(m\)條邊的無向圖,以及一個整數\(k\)。你須要給每一條邊染一種顏色。顏色種類無限多,每種顏色最多用兩次,且對於任意一個點,和它相連的邊的顏色不能超過\(k\)種。求一種可行的染色方案。
\(n,m\leq600\)。
\(Solution\)
設\(dgr_i\)爲\(i\)的度數。那麼若是\(dgr_i\leq k\),\(i\)連的邊的顏色是什麼都無所謂。不然須要讓\(2(dgr_i-k)\)條邊兩兩配對。
一條邊在一個點處配對了就不能在另外一個點處配對。對每條邊\(i\ (u,v)\)連邊\((u\to i,1),(v\to i,1),(i\to T,1)\),每一個點\(x\)連邊\((S\to x,\max(0,\ 2(d_x-k)))\),看最大流是否等於\(\sum\max(0,\ 2(d_x-k))\)便可。
不寫代碼惹。blog