题库中也有不少我想不出来的模拟赛的题目。作仍是必要的。作本身的题目 时间很紧 想一想本身的文化课 我又没有那么强 我必须得刷.ios
LINK:水题一道c++
发现是一道计数题 计数题拿高分的才是王者,可是 计数题不取模就是在耍流氓了...很久没写高精了 顺便写写。数组
此题 咱们发现 无论怎么搞每一个教室都须要两我的咱们不妨 先让m-=2*n; 而后就是剩下的m我的的分配问题了 感受是组合数其实并不尽然。由于显然不对...ide
这样 对于一我的来讲他有n种可能 那么m我的呢?m^n 可是显然有重复如第一我的选第二个 第二我的选第一个 和第一我的选第一个第二我的选第二个这样是等效的...优化
考虑排列数对咱们答案的影响,无论排列的话其实除以一个m!便可。而后成功想错 这样的作法是错误的。由于这有重复除以m!根本什么也不是。。。没有什么排列数...spa
正解是隔板法 再次隔板 设当前人数为 s=m-2*n 那么其实有将s我的分到n个房间中 且每一个房间总和为s 也就是说 x1+x2+x3+x4+...=s 这显然是隔板法了...设计
注意到x可能为0 因此再补板n-1 空隙 仍是插板n-1个便可。m为500 用头想都是高精。C(s+n-1,n-1) 。。。想10min都没怎么发现这个模型藏得有点深。3d
约分是必要的 分解质因数再约分 就变成高精乘单精了。rest
//#include<bits/stdc++.h> #include<iostream> #include<iomanip> #include<ctime> #include<cstring> #include<string> #include<ctime> #include<cctype> #include<cstdio> #include<utility> #include<queue> #include<stack> #include<deque> #include<map> #include<set> #include<bitset> #include<vector> #include<algorithm> #include<cstdlib> #define ll long long using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010; int s0,s1,s2; int p0[MAXN],p1[MAXN],p2[MAXN]; int b[MAXN],top; int n,m,s,c; inline void insert(int x,int *a) { int s=x; for(int i=2;i*i<=x;++i) while(s%i==0) { s=s/i; ++a[i]; } if(s>1)++a[s]; } inline void mul(int x) { int res=0; for(int i=1;i<=top;++i) { b[i]=b[i]*x; b[i]+=res; res=b[i]/10; b[i]=b[i]%10; } while(res) { b[++top]=res; res=b[top]/10; b[top]=b[top]%10; } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); if(m<2*n){puts("0");return 0;} s=m-2*n+n-1;c=n-1;//求 C(s,c); for(int i=2;i<=s;++i)insert(i,p0); for(int i=2;i<=c;++i)insert(i,p1); for(int i=2;i<=s-c;++i)insert(i,p2); b[++top]=1; for(int i=2;i<=s;++i) { p0[i]=p0[i]-p1[i]-p2[i]; while(p0[i]) { --p0[i]; mul(i); } } for(int i=top;i>=1;--i)printf("%d",b[i]); return 0; }
LINK:免费馅饼code
咕了 这么长时间了 也该写写了... 看起来很显然是dp 可是还要输出方案...(那也很水
第一次写状态转移写错了 ,惊了 瞎写了一个状态转移..感受不太对..固然f[i][j]表示第i秒到了第j个位置的最大价值 f[i][j]=max(f[i-1][j-1],f[i-1][j-2]...)而后 馅饼的话开vector记录下来,到达这个位置就累加上价值便可。
有一个坑点是 对于一个馅饼 降低高度为 h-1 题目中说道 刚好在某一秒末和人相遇才行也就是 h-1%v!=0的都不能要。还有一个坑点是 方案要从最后一个馅饼开始找而不是最后一秒开始...
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define R register #define db double #define max(x,y) ((x)>(y)?(x):(y)) using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=110,maxn=2010; int n,m,cnt,maxx,ans; int st[MAXN],top; struct wy { int s,x,v,p; }t[maxn]; int f[maxn][MAXN];//f[i][j]表示第i秒在x这个位置所能得到的最大分数 int w[maxn][MAXN]; vector<int>g[maxn]; inline int abs(int x){return x>0?x:-x;} inline void get_path(int x,int y) { if(!x)return; st[++top]=-(w[x][y]-y); get_path(x-1,w[x][y]); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); int s,x,v,p,h=m;--h; while(scanf("%d",&s)==1) { x=read();v=read();p=read(); if(h%v!=0)continue; t[++cnt]=(wy){s,x,v,p}; int st=s+h/v; g[st].push_back(cnt); maxx=max(maxx,st); } memset(f,0xcf,sizeof(f)); f[0][(n>>1)+1]=0; for(int i=1;i<=maxx;++i) { for(int j=1;j<=n;++j) { for(int k=max(1,j-2);k<=min(n,j+2);++k) { if(f[i][j]<f[i-1][k]) { f[i][j]=f[i-1][k]; w[i][j]=k; } } } for(int k=0;k<(int)g[i].size();++k) { int r=g[i][k]; f[i][t[r].x]+=t[r].p; } } for(int i=1;i<=n;++i)ans=max(ans,f[maxx][i]); printf("%d\n",ans); for(int i=1;i<=maxx;++i) { for(int j=1;j<=n;++j) { if(f[i][j]==ans) { get_path(i,j); for(int k=top;k>=1;--k)printf("%d\n",st[k]); return 0; } } } return 0; }
update :其实这个也能够不用vector存咱们利用排序后时间从大到小的顺序便利整个数组便可 省掉空间和时间...
LINK:字符合并
早就想写这道题了 受不了了 这道题 是水题..我坚信。(而后想了30min 被打脸...
仍是 字符串之间进行合并 值得一提的是此次只有01串 每k个字符能够合成一个字符 存在一个命题是 若是(n-1)%(k-1)==0的话那么则有 最后合并剩下一个字符 (我也不知道是怎么证实的..
首先仍是设计状态吧 反正是区间dp 转移着实有点困难 但在我坚持不懈的努力之下 仍是成功的进行转移了 可是不知道对不对 感受正确性仍是有的..
f[i][j][k]表示 由i到j状态为k的最大 代价 那么转移就是区间dp 而后 枚举断点 惟一不一样的是还须要枚举左边状态s1 右边状态s2 而后merge一下便可 merge有点难写 可是仔细思考仍是能够O(1)的...
可是这样的复杂度是n^3*2^k*2^k=n^3*4^k; 显然过不了1e12 。
这里先放暴力(我不知道对不对 不想交):发现暴力是瞎写的 不放了
固然 我以为这个merge写的还算很精辟/cy.(精辟锤子 写错了。
考虑优化一个比较显然的地方是每次咱们枚举状态的时候其实没必要要枚举到1<<(k-1) 由于对于一个区间来讲其固定的状态大小是存在的 (n-1)%(k-1)+1其实就是这个东西了...每8次一循环每一个循环的复杂度是2^1-1+2^2-1+...2^7-1加起来是2^8那么这样其实复杂度骤降8倍..
还有一个不太明显的优化是 进行状态合并的时候 若是没有合成 也就是不到k个字符的合成 左边合成和右边合成其实都是等效的 因此咱们能够省掉右边的合并钦定左边进行合并便可。
那么就是 咱们只在意右边合成了1或者0 至于更多的能够无论交给左边去合成便可。这样复杂度降为n^3*2^k/8 能够calc一下 发现能够过了.
这样对于左边的状态咱们仍是能够利用(n-1)%(k-1)这个东西来剪枝。
那么这道题也就作完了 我看了一眼书才发现的解法 我也没想到第二个优化 可能脑子有点乱..之后dp的时候要清醒 别看书或看题解..这点小优化仍是能够想出来的我以为。
一直wa 发现是合并的时候 没有判断准确合并的大小和长度 不是随便合并的 0000和1能够合并可是状态得本身看 有的时候0 可能表明着0000什么的我忽略了这一点。
固然 得开long long 题目描述不清楚 固然所有开long long会T (我真不容易写一道题。注意不要全开long long 常数是int的两倍 因而把for里的改为int就过了。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define R register #define db double #define max(x,y) ((x)>(y)?(x):(y)) using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=301,maxn=8; ll n,k,ans; int a[MAXN],p[1<<maxn]; int s[1<<maxn]; ll f[MAXN][MAXN][1<<maxn]; //f[i][j][k] 表示由区间i到区间j合成后状态为k的最大值 signed main() { //freopen("1.in","r",stdin); n=read();k=read(); memset(f,0xcf,sizeof(f)); for(int i=1;i<=n;++i)a[i]=read(),f[i][i][a[i]]=0; for(int i=0;i<(1<<k);++i) s[i]=read(),p[i]=read(); for(int len=2;len<=n;++len) { for(int i=1;i<=n-len+1;++i) { int j=i+len-1; for(int l=i;l<j;++l) { int len2=j-l; if((len2-1)%(k-1))continue; int len1=l-i+1; len1=(len1-1)%(k-1)+1; for(int s1=0;s1<(1<<len1);++s1) { if(f[i][l][s1]<0)continue; if(f[l+1][j][0]>=0) { int state=s1<<1; if(len1==k-1) f[i][j][s[state]]=max(f[i][j][s[state]],f[i][l][s1]+f[l+1][j][0]+p[state]); else f[i][j][state]=max(f[i][j][state],f[i][l][s1]+f[l+1][j][0]); } if(f[l+1][j][1]>=0) { int state=s1<<1|1; if(len1==k-1) f[i][j][s[state]]=max(f[i][j][s[state]],f[i][l][s1]+f[l+1][j][1]+p[state]); else f[i][j][state]=max(f[i][j][state],f[i][l][s1]+f[l+1][j][1]); } } } } } for(int i=0;i<(1<<(k-1));++i) ans=max(ans,f[1][n][i]); printf("%lld\n",ans); return 0; }
LINK:分数规划生成树
其实就是想让咱们找出一个最小的生成树罢了,直接prim会比克鲁斯卡尔快一点。注意这里须要二分一下比值由于咱们很差直接求出这个比值。
因此考虑先二分,而后权值就有了咱们想办法让mid减少的那个方式生成树便可。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define EPS 1e-4 #define mp make_pair #define F first #define S second using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010; int n; struct wy { int x,y,z; }t[MAXN]; int vis[MAXN]; db a[MAXN][MAXN],d[MAXN]; priority_queue<pair<db,int> > q; inline int abs(int x){return x<0?-x:x;} inline db dis(int x,int y) { return sqrt(1.0*(t[x].x-t[y].x)*(t[x].x-t[y].x)+1.0*(t[x].y-t[y].y)*(t[x].y-t[y].y)); } inline int check(db w) { db ans=0; for(int i=1;i<=n;++i)d[i]=INF*1.0,vis[i]=0; q.push(mp(0,1)); while(q.size()) { int x=q.top().S; if(vis[x]) { q.pop(); continue; } ans=ans-q.top().F; vis[x]=1; for(int i=1;i<=n;++i) { if(vis[i])continue; db dd=abs(t[x].z-t[i].z)-a[x][i]*w; if(d[i]>dd) { d[i]=dd; q.push(mp(-d[i],i)); } } } if(ans<=0)return 1; return 0; } int main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i) { int x,y,z; x=read();y=read();z=read(); t[i]=(wy){x,y,z}; } for(int i=1;i<=n;++i) for(int j=i+1;j<=n;++j) a[i][j]=a[j][i]=dis(i,j); db l=0,r=100; while(l+EPS<r) { db mid=(l+r)/2; if(check(mid))r=mid; else l=mid; } printf("%.3lf",l); return 0; }
LINK:pigs有点神奇的题目 看起来很复杂 但其实我还真不会 我有一个作法是拆点而后对于每一个人都作一次最大流 可是仍是没法实现猪交换的时候的合法性。
作法比较强大 是这样的咱们很难完成在存在牛的点的状况下实现牛之间的交换由于考虑这个顺序比较难以控制 可能较早的点会用较晚的点的边因此这很很差写,咱们不妨不建牛点 把牛放到一块而后表示牛能够任意互换,这样就不会出现上述状况了。综上咱们 每次都将一堆牛直接连向一我的 若是下次还有人连相同的牛的话就把上次连的人连向当前这我的便可。
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define db double #define mp make_pair using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=1010; int n,m,S,T,ans,len=1,t,h; int a[MAXN],b[MAXN]; int lin[MAXN],nex[MAXN*100<<1],ver[MAXN*100<<1],e[MAXN*100<<1]; int q[MAXN],vis[MAXN],cur[MAXN]; inline void add(int x,int y,int z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline int bfs() { h=t=0; for(int i=1;i<=T;++i)vis[i]=0,cur[i]=lin[i]; q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; vis[tn]=vis[x]+1; q[++t]=tn; if(tn==T)return 1; } } return 0; } inline int dinic(int x,int flow) { if(x==T)return flow; int rest=flow,k; for(int i=cur[x];i&&rest;i=nex[i]) { int tn=ver[i]; cur[x]=i; if(vis[tn]==vis[x]+1&&e[i]) { k=dinic(tn,min(flow,e[i])); if(!k){vis[tn]=0;continue;} e[i]-=k;e[i^1]+=k;rest-=k; } } return flow-rest; } int main() { //freopen("1.in","r",stdin); m=read();n=read(); S=n+1;T=S+1; for(int i=1;i<=m;++i)a[i]=read(); for(int i=1;i<=n;++i) { int x=read(),cnt=0; for(int j=1;j<=x;++j) { int y=read(); if(b[y])add(b[y],i,INF); else cnt+=a[y]; b[y]=i; } x=read(); add(S,i,cnt); add(i,T,x); } int flow=0; while(bfs())while((flow=dinic(S,INF)))ans+=flow; printf("%d\n",ans); return 0; }
LINK:足球积分 看起来是个贪心。其实应该就是贪心了。很简单对于求最大值咱们能赢就赢不要平局由于赢得话比平局得分高 。
输的话恰好相反 分类讨论一下便可。想了一下求最小值还真的有很大的难度 思考良久发现把全部状况搞出来取最小值便可。
很不错的贪心 虽然wa了不少次 下次在提交以前应该手玩一些数据才对。细节 有点ex 不过剩下的就没什么了。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<cmath> #include<cctype> #include<algorithm> #include<cstdlib> #include<queue> #include<stack> #include<set> #include<bitset> #include<vector> #include<map> #include<deque> #include<utility> #define INF 1000000010 #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define mp make_pair #define ll long long using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline ll read() { ll x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const ll MAXN=100010,maxn=MAXN<<1; ll x,y,n; ll ans,ans1; signed main() { //freopen("1.in","r",stdin); while(scanf("%d%d%d",&x,&y,&n)==3) { ans=ans1=0; ll xx=x,yy=y,nn=n; if(n==1) { if(x>y)ans=ans1=3; if(x==y)ans=ans1=1; printf("%lld %lld\n",ans,ans1); continue; } //先求最大值 ll w=n-1; ll v=min(w,x); ans+=v*3; w-=v; x-=v; if(x>y)ans+=3; if(x==y)++ans; ans+=w; printf("%lld ",ans); //再求最小值 if(xx>yy) { ans1+=3; --nn; v=min(yy,nn); nn-=v; ans1+=nn; printf("%lld\n",ans1); continue; } --nn; if(xx==yy) { if(nn>=3&&yy>=3) { ans1+=3; v=min(yy,nn); nn-=v; ans1+=nn; printf("%lld\n",ans1); } else { ++ans1; ans1+=nn; printf("%lld\n",ans1); } } else//对于 xx<yy 能够考虑把全部状况表示出来取min { ll w1=3,w2=1,w3=0; w1=w1+(nn-min(nn,yy)); yy-=xx; w2=w2+(nn-min(nn,yy)); --yy; w3=w3+(nn-min(nn,yy)); ans1=min(w1,w2); ans1=min(ans1,w3); printf("%lld\n",ans1); } } //cout<<w1<<' '<<w2<<' '<<w3<<endl; return 0; }
LINK:背包的方案数问题。 求不适用第一个物品且装满背包的方案数 再求不用第二个物品的方案数 一直求到第n个?
一个比较显然的思路是暴力 n^2m的暴力 考虑优化这个过程。考虑 咱们每次把有用的东西保存下来。也就是说 咱们先对总体进行一次01背包方案数 而后 想办法求出第一个除去第一个物品的方案数 观察 把dp式加法换成减法便可。考虑一下实际的意义。
咱们先广义的分析:作背包 和 物品的顺序无关 因此咱们能够想象成第一个物品是最后一个加进去的 因此能够看出 这样的作法显然正确。
狭义的想:这很显然。
当时我写的无脑暴力 仍是保存一下吧 。
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<iomanip> #include<algorithm> #include<ctime> #include<map> #include<vector> #include<stack> using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int maxn=1014; int n,m; int w[maxn],f[10007],ans=0; int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;i++)w[i]=read(),ans+=w[i]; f[0]=1; for(int k=1;k<=n;k++) { memset(f,0,sizeof(f)); f[0]=1; for(int i=1;i<=n;i++) { if(ans-w[i]<m)break; for(int j=m;j>=w[i];j--) { if(i==k)continue; f[j]=(f[j]%maxn+f[j-w[i]]%maxn)%maxn; } } printf("%d ",f[m]%maxn); if(k==n)printf("\n"); } return 0; }
这是 很顺利的 倒序背包清空一下便可。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define mod 1014 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=10010; int n,m; int a[MAXN],f[MAXN],w[MAXN]; int main() { //freopen("FILE.in","r",stdin); //freopen("FILE.out","w",stdout); n=read();m=read(); f[0]=1; for(int i=1;i<=n;++i)a[i]=read(); for(int i=1;i<=n;++i) for(int j=m;j>=a[i];--j) f[j]=(f[j]+f[j-a[i]])%mod; //for(int i=1;i<=m;++i)cout<<f[i]<<endl; for(int i=1;i<=n;++i) { if(m<a[i]){printf("%d\n",f[m]);continue;} for(int j=0;j<=m;++j)w[j]=f[j]; for(int j=a[i];j<=m;++j)f[j]=(f[j]-f[j-a[i]])%mod; printf("%d ",((f[m]+mod)%mod)); for(int j=0;j<=m;++j)f[j]=w[j]; } return 0; }
LINK:最大速度 中级搜索。 这题 搜索起来比较麻烦。
因为到达每一个点还有方向考虑把方向也存起来 考虑dij 开跑 可是连续转两次也算掉头 因此还难免记录上次左转仍是右转。
注意 大根堆别写错 方向别搞错 预处理要仔细 dij 换成spfa 咱们发现 有状态都是0 可是一个0状态能更新一个0状态 因此只能spfa了..
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000010 #define ll long long #define mp(x,y) make_pair(x,y) #define un unsigned #define db double #define EPS 1e-5 #define mod 1014 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=31; int st,en,op,s1,s2,vv,n,m,cnt,ans; int dx[5]={0,1,-1,0,0}; int dy[5]={0,0,0,1,-1}; int f[MAXN][MAXN][5],fa[MAXN*MAXN]; int vis[MAXN][MAXN][5],id[MAXN][MAXN]; int d[5][5][2],v[5][5],g[5][5]; struct wy { int x,y,op; int v,pre; int friend operator <(wy a,wy b) { return a.v<b.v; } }; char a[MAXN][MAXN]; priority_queue<wy> q; inline int getfather(int x){return x==fa[x]?x:fa[x]=getfather(fa[x]);} inline void bfs() { while(q.size()) { wy s=q.top();q.pop(); //if(vis[s.x][s.y][s.op])continue; //vis[s.x][s.y][s.op]=1; for(int i=1;i<=4;++i) { int xx=s.x+d[s.op][i][0]; int yy=s.y+d[s.op][i][1]; if(xx<1||yy<1||xx>n||yy>m)continue; if(a[xx][yy]=='.')continue; int w=max(0,s.v+v[s.op][i]); if(g[s.op][i]==s.pre&&s.pre>0)w=0; if(f[xx][yy][i]<w) { f[xx][yy][i]=w; q.push((wy){xx,yy,i,w,g[s.op][i]}); } } } } int main() { //freopen("1.in","r",stdin); n=read();m=read();vv=read(); memset(f,0xcf,sizeof(f)); for(int i=1;i<=n;++i) { scanf("%s",a[i]+1); for(int j=1;j<=m;++j) { id[i][j]=++cnt;fa[cnt]=cnt; if(a[i][j]!='.'&&a[i][j]!='#'&&a[i][j]!='F') { if(a[i][j]=='E')op=1; if(a[i][j]=='W')op=2; if(a[i][j]=='N')op=3; if(a[i][j]=='S')op=4; st=i,en=j; } if(a[i][j]=='F')s1=i,s2=j; } } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(a[i][j]=='.')continue; for(int k=1;k<=4;++k) { int x=i+dx[k]; int y=j+dy[k]; if(x<1||y<1||x>n||y>m)continue; if(a[x][y]=='.')continue; int xx=getfather(id[x][y]); int yy=getfather(id[i][j]); fa[xx]=yy; } } if(getfather(fa[id[st][en]])!=getfather(fa[id[s1][s2]])) { puts("-1");return 0; } f[st][en][op]=vv; d[1][1][0]=0;d[1][1][1]=1;v[1][1]=1;g[1][1]=0; d[1][2][0]=0;d[1][2][1]=0;v[1][2]=-INF;g[1][2]=-1; d[1][3][0]=0;d[1][3][1]=0;v[1][3]=-35;g[1][3]=1;//向左 d[1][4][0]=0;d[1][4][1]=0;v[1][4]=-40;g[1][4]=2;//向右 d[2][1][0]=0;d[2][1][1]=0;v[2][1]=-INF;g[2][1]=-1; d[2][2][0]=0;d[2][2][1]=-1;v[2][2]=1;g[2][2]=0; d[2][3][0]=0;d[2][3][1]=0;v[2][3]=-40;g[2][3]=2; d[2][4][0]=0;d[2][4][1]=0;v[2][4]=-35;g[2][4]=1; d[3][1][0]=0;d[3][1][1]=0;v[3][1]=-40;g[3][1]=2; d[3][2][0]=0;d[3][2][1]=0;v[3][2]=-35;g[3][2]=1; d[3][3][0]=-1;d[3][3][1]=0;v[3][3]=1;g[3][3]=0; d[3][4][0]=0;d[3][4][1]=0;v[3][4]=-INF;g[3][4]=-1; d[4][1][0]=0;d[4][1][1]=0;v[4][1]=-35;g[4][1]=1; d[4][2][0]=0;d[4][2][1]=0;v[4][2]=-40;g[4][2]=2; d[4][3][0]=0;d[4][3][1]=0;v[4][3]=-INF;g[4][3]=-1; d[4][4][0]=1;d[4][4][1]=0;v[4][4]=1;g[4][4]=0; q.push((wy){st,en,op,vv,-2}); bfs(); for(int i=1;i<=4;++i)ans=max(ans,f[s1][s2][i]); printf("%d\n",ans); return 0; }
LINK:序列游戏 疯狂的刷题之中尽可能不看题解TAT。
两种问题 1 输出字典序第k小的序列 2 输出某个全排列 的字典序编号。
也就是两个问题 恰好互反。询问有10000次 但序列的长度只有20。
遇到这种序列问题先分析一下数字典序须要什么东西QuQ 由于没有性质作题 很难受的。
我想了半天逆序对 但我以为和逆序对无关关键是字典序。值得一提的是这里序列都是全排列。
先思考一下Q1 无疑 字典序第k小 字典序之和当前数字大小有关 那么咱们逐位考虑 对于当前位置i 能放i就放 不能放的话考虑 其放哪qwq
逐位考虑有效 若是当前位不行 那就 减少了 屡次阶乘的方案 这样考虑下去便可。
重点是第二问 如何导出 仍是逐位考虑 而后发现 了一些小性质 对于第一个位置 不合法的话咱们把全部对后面的影响都减掉 默认后面所有都是单调递减的。
再继续向后面扫若是不是单调递减的 咱们还须要加回来 而后逐位判断是否加便可。
#//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define ll long long #define db double #define pb push_back #define un unsigned using namespace std; inline ll read() { ll x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const ll MAXN=25; ll n,Q,k,ans; char a[2]; ll b[MAXN],fac[MAXN],vis[MAXN]; signed main() { //freopen("1.in","r",stdin); n=read();Q=read(); fac[n+1]=1; for(ll i=n;i;--i)fac[i]=fac[i+1]*(n-i+1); for(ll i=1;i<=Q;++i) { scanf("%s",a+1); if(a[1]=='P') { k=read(); memset(vis,0,sizeof(vis)); for(ll j=1;j<=n;++j) { for(ll m=1;m<=n;++m) { if(vis[m])continue; if(fac[j+1]>=k) { vis[m]=1; b[j]=m; break; } else k-=fac[j+1]; } } for(ll j=1;j<=n;++j)printf("%lld ",b[j]); puts(""); } else { ll flag=0;ans=0; for(ll j=1;j<=n;++j) { b[j]=read(); if(b[j]!=j&&!flag){flag=j;ans+=(b[j]-j+1)*fac[j+1];} } if(!flag){puts("1");continue;} memset(vis,0,sizeof(vis)); for(ll j=1;j<=n;++j) { if(j>flag) { ll w=0; for(ll m=b[j];m<=n;++m) { if(vis[m])continue; ++w; } ans-=(w-1)*fac[j+1]; } vis[b[j]]=1; } printf("%lld\n",ans); } } return 0; }
LINK:在线查询第k大 在线查询第k大 每次针对全局找第k大 这个很好写吧 而后其实就是单点修改了。
我再一次手懒的没写splay 直接主席树水了 我好菜啊 连平衡树都 码不出来 /kk
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define INF 1000000000 #define inf 1000000000 #define ll long long #define mod 1000000007 #define db double #define pb push_back #define un unsigned #define l(p) t[p].l #define r(p) t[p].r #define sum(p) t[p].sum using namespace std; inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=100010; int n,id,Q; int a[MAXN],maxx=INF,minn,rt; char b[2]; struct wy { int l,r; int sum; }t[MAXN*30<<1]; inline void insert(int &p,int l,int r,int x,int w) { if(!p)p=++id; if(l==r){sum(p)+=w;return;} int mid=(l+r)>>1; if(x<=mid)insert(l(p),l,mid,x,w); else insert(r(p),mid+1,r,x,w); sum(p)=sum(l(p))+sum(r(p)); } inline int ask(int p,int l,int r,int k) { if(sum(p)<k)return -1; if(l==r)return l; int mid=(l+r)>>1; if(sum(r(p))>=k)return ask(r(p),mid+1,r,k); return ask(l(p),l,mid,k-sum(r(p))); } signed main() { //freopen("1.in","r",stdin); n=read(); for(int i=1;i<=n;++i)a[i]=read(),insert(rt,minn,maxx,a[i],1); Q=read(); for(int i=1;i<=Q;++i) { scanf("%s",b+1); int x=read(),y; if(b[1]=='A') { y=read(); insert(rt,minn,maxx,a[x],-1); a[x]-=y; if(a[x]<=0)continue; insert(rt,minn,maxx,a[x],1); } if(b[1]=='C') { y=read(); insert(rt,minn,maxx,a[x],-1); a[x]+=y; insert(rt,minn,maxx,a[x],1); } if(b[1]=='Q')printf("%d\n",ask(rt,minn,maxx,x)); } printf("%d\n",sum(1)); return 0; }
我靠???我电脑关机了 写了一堆东西没了 自闭 3min。
LINK:一个巨难无比的区间dp 写这个题 整我的都快死了 不过打代码的 感受仍是挺不错的。
这道题 我想了一些状态都是没法完成一些操做 考虑正解 设f[i][j][k]表示在i到j这个区间前加k个ai珠子的最小代价。
这个状态是必要的消掉柱子的时候就两端拼起来就是在某个地方添柱子的 决策 因此这个状态是必要的 。
而后 值得注意的是消掉柱子咱们只须要判断消掉中间的一段而后两端连起来就好了 而后 两端具体一点是指 1个球和后面的一段 由于区间的决策具备最优性此时枚举决策一段和一个球能够近乎当作等效 因此 这样转移的dp是正确的。
因为没有明显的递推关系 因此须要记搜一下。
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double #define pii pair<ll,ll> #define mk make_pair using namespace std; char *fs,*ft,buf[1<<15]; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=110; int n,K; int f[MAXN][MAXN][5];//f[i][j][k]表示在i到j这个区间前加k个ai珠子的最小代价 int a[MAXN]; inline int dfs(int l,int r,int k)//描述整个状态 { if(f[l][r][k]!=-1)return f[l][r][k]; if(l==r)return f[l][r][k]=K-(k+1); if(l>r)return 0; int ans=INF; if(k+1==K)ans=min(ans,dfs(l+1,r,0));//发现能够消掉 if(k+1<K)ans=min(ans,dfs(l,r,k+1)+1);//原地 加一个球 for(int i=l+1;i<=r;++i) if(a[l]==a[i]) ans=min(ans,dfs(l+1,i-1,0)+dfs(i,r,min(K-1,k+1))); return f[l][r][k]=ans; } int main() { //freopen("1.in","r",stdin); n=read();K=read(); memset(f,-1,sizeof(f)); for(int i=1;i<=n;++i)a[i]=read(); printf("%d\n",dfs(1,n,0)); return 0; } /** * ┏┓ ┏┓+ + * ┏┛┻━━━┛┻┓ + + * ┃ ┃ * ┃ ━ ┃ ++ + + + * ████━████+ * ◥██◤ ◥██◤ + * ┃ ┻ ┃ * ┃ ┃ + + * ┗━┓ ┏━┛ * ┃ ┃ + + + +Code is far away from * ┃ ┃ + bug with the animal protecting * ┃ ┗━━━┓ 神兽保佑,代码无bug * ┃ ┣┓ * ┃ ┏┛ * ┗┓┓┏━┳┓┏┛ + + + + * ┃┫┫ ┃┫┫ * ┗┻┛ ┗┻┛+ + + + */