【noip】跟着洛谷刷noip题2
noip好难呀。
上一个感觉有点长了,重开一个。
36.Vigenère 密码
粘个Openjudge上的代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include <cstdlib>
#include <algorithm>
#include <iomanip>
using namespace std;
char a[101],s[1001],b[1001];
int c[1001],j;
int main()
{
int la,ls,i,j;
gets(a);
gets(s);
la=strlen(a);
ls=strlen(s);
for(i=0;i<la;i++)
{if(a[i]<=90)
a[i]=a[i]+32;}
for(i=0;i<la;i++)
c[i]=a[i]-97;
for(i=0;i<ls;i++)
c[i+la]=c[i];
for(i=0;i<ls;i++)
{
b[i]=s[i]-c[i];
if((s[i]>=65&&b[i]<65)||(s[i]>=97&&b[i]<97))
b[i]=b[i]+26;
}
puts(b);
return 0;
}
37.国王游戏
贪心。手动艾特LLJ。
粘一个别人的代码。
#include<iostream> #include<algorithm> #include<cstring> using namespace std; int n,i,sum[10000],kx,ky,t[10000],ans[10000]; struct node { int x; int y; }a[1001];//每个大臣的左右手数值 bool cmp(node a,node b) { return a.x*a.y<b.x*b.y;//按照左右手乘积排序 } bool judge()//用来判断当前大臣的结果是不是比ans大 { int i; for (i=1;i<=t[0];++i) if (t[i]>ans[i]) return true; else if (t[i]<ans[i]) return false; return false; } void mul(int x)//高乘 { int g=0,i; for (i=1;i<=sum[0];++i) { sum[i]=sum[i]*x+g; g=sum[i]/10; sum[i]=sum[i]%10; } while (g!=0) { ++sum[0]; sum[sum[0]]=g%10; g/=10; } } void divv(int x)//高除,附带比较当前大臣答案和ans的大小关系 { int num=0,i=sum[0]+1,s=0; memset(t,0,sizeof(t)); while (num<x) { --i; num=num*10+sum[i]; } t[0]=i; for (;i>=1;--i) { t[++s]=num/x; num=num%x*10+sum[i-1]; } //高除,其中t保存当前大臣的结果 if (t[0]>ans[0]||t[0]==ans[0]&&judge())//.若比ans大则更新ans { ans[0]=t[0]; for (i=1;i<=t[0];++i) ans[i]=t[i]; } } int main() { cin>>n; cin>>kx>>ky; sum[1]=kx; sum[0]=1; ans[0]=1; ans[1]=0; //下标0保存长度 //sum用来计算到第i个大臣的左手乘积 for (i=1;i<=n;++i) cin>>a[i].x>>a[i].y; sort(a+1,a+n+1,cmp); for (i=1;i<=n;++i) { mul(a[i].x); divv(a[i].y*a[i].x);//因a[i].x当前已经乘过所以除的时候要再除一遍 } for (i=1;i<=ans[0];++i) cout<<ans[i]; }
38.同余方程
做不来,是不是没救了。。。
裸的扩展欧几里得。
注意扩欧先除再乘以免爆炸。。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> using namespace std; typedef long long LL; LL a,b,x,y,d; void exgcd (LL a,LL b,LL &x,LL &y,LL &d) { if(!b) {d=a; x=1; y=0; return;} exgcd(b,a%b,y,x,d); y=y-(a/b*x); } int main() { cin>>a>>b; exgcd(a,b,x,y,d); cout<<(x+b)%b<<endl; return 0; }
39.借教室
第一眼线段树模板,听说会被卡,卡卡常应该能过吧。
正解 二分+差分。
一开始把某个n写成m,一直WA第18个点。。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> using namespace std; typedef long long LL; const int maxn=1e6+5; int n,m,a[maxn],b[maxn],ll[maxn],rr[maxn],c[maxn]; void read(int &x) { char ch=getchar(); int ret=0,f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; x=ret*f; } void init() { read(n); read(m); for(int i=1;i<=n;i++) read(a[i]); for(int i=1;i<=m;i++) { read(c[i]); read(ll[i]); read(rr[i]); } } int ok(int x) { for(int i=1;i<=n;i++) b[i]=a[i]-a[i-1]; for(int i=1;i<=x;i++) { int l=ll[i],r=rr[i]; b[l]-=c[i]; b[r+1]+=c[i]; } int now=0; for(int i=1;i<=n;i++) { now+=b[i]; if(now<0) return 0; } return 1; } void work() { int l=0,r=m,ans=0; while(l<=r) { int mid=(l+r)>>1; if(!ok(mid)) ans=mid,r=mid-1; else l=mid+1; } if(!ans) printf("0\n"); else { printf("-1\n"); printf("%d\n",ans); } } int main() { init(); work(); return 0; }
40.津津的储蓄计划
Openjudge打不开啦,随便抄个别人的代码水一下。
#include <iostream> using namespace std; int main() { const int m=300; bool flag=true; int h=0,s=0; int a; for(int i=1;i<13;i++) { cin>>a; h+=m; if(h<a) { cout<<-i; flag=false; break; } int left=h-a; h=left%100; s+=left-h; } if(flag) cout<<s*1.2+h; return 0; }
41.合并果子
优先队列水过。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> const int maxn=10005; using namespace std; typedef long long LL; int n,x,ok[maxn],ans; void read(int &x) { char ch=getchar(); int ret=0,f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; x=ret*f; } priority_queue<int,vector<int>,greater<int> >que; int main() { read(n); for(int i=1;i<=n;i++) { read(x); que.push(x); } while(!que.empty()) { int a=que.top(); que.pop(); if(que.empty()) break; int b=que.top(); que.pop(); ans+=a+b; que.push(a+b); } printf("%d\n",ans); return 0; }
31.合唱队形
抄个代码水一水
#include<iostream> #include<cmath> using namespace std; int num[1010];//每个人的身高 int ans=1;//最后留下的人数 int sum1[1010],sum2[1010];//升序列和降序列中留下的人数 int main(){ int n; cin>>n; for(int i=1;i<=n;i++){ cin>>num[i];//输入 sum1[i]=1;//留下的人数,至少为1 sum2[i]=1;} for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(num[i]>num[j])//如果i位置的值大于j位置的,这是可以作为上升子序列 sum1[i]=max(sum1[i],sum1[j]+1);//比较不取i位置和取i位置哪个留下的人多 for(int i=n;i>=1;i--)//最小降序列,反着枚举 for(int j=n;j>i;j--) if(num[i]>num[j]) sum2[i]=max(sum2[i],sum2[j]+1); for(int i=1;i<=n;i++) ans=max(ans,(sum2[i]+sum1[i]-1));//ans是留下的人数,和每一种方案留下的人数比较。减一是中间的人重复计数。 cout<<n-ans<<endl;//n是总人数,ans留下的人数 return 0; }
32.统计数字
同上,装出自己做了题的样子。
#include <iostream> #include <algorithm>//快排头文件 using namespace std; bool cmp (int a,int b)//养成写排序规则的好习惯 { return a<b; } int main () { int n; cin>>n; int a[n]; for (int i=0;i<n;i++) cin>>a[i]; sort (a,a+n,cmp);//进行升序排序 int ans=1;//计数变量 for (int i=1;i<n;i++) { if (a[i-1]==a[i]) ans++;//若两两相等,计数加一 else {cout<<a[i-1]<<' '<<ans<<endl;ans=1;}//不等就输出,重新赋值 } cout<<a[n-1]<<' '<<ans<<endl;//因为最后一个数字还未输出,所以输出 return 0; }
33.天天爱跑步
一开始觉得超级难写,然而写起来意外的顺利而且一A了,就比较开心。
这两天碰到不少差分,才发现其实自己以前根本不知道差分到底是啥,差分真是神奇的东西。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> const int maxn=300000; using namespace std; typedef long long LL; int n,m,w[maxn],up[maxn<<1],dn[maxn<<1]; vector<int>vcu[maxn][2]; vector<int>vcd[maxn][2]; void read(int &x) { int ret=0,f=1; char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; x=ret*f; } int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1]; void add(int u,int v) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; } int f[maxn][20],R[maxn]; void dfs1(int x,int fa) { f[x][0]=fa; R[x]=R[fa]+1; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa){ dfs1(to[i],x); } } void make_st() { for(int i=1;i<=19;i++) for(int x=1;x<=n;x++) { f[x][i]=f[f[x][i-1]][i-1]; } } int get_lca(int x,int y,int &tp) { int fl=0; if(R[x]<R[y]) {fl=1; swap(x,y);} for(int i=19;i>=0;i--) if(f[x][i]&&R[f[x][i]]>R[y]) x=f[x][i]; if(f[x][0]==y) {tp=x; return f[x][0];} if(f[x][0]&&R[f[x][0]]>=R[y]) x=f[x][0]; for(int i=19;i>=0;i--) if(f[x][i]&&f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; if(fl) tp=x; else tp=y; return f[x][0]; } void init() { read(n); read(m); for(int i=1;i<n;i++) { int u,v; read(u); read(v); add(u,v); } dfs1(1,0); make_st(); for(int i=1;i<=n;i++) read(w[i]); for(int i=1;i<=m;i++) { int x,y,z,tp; read(x); read(y); z=get_lca(x,y,tp); int len=R[x]-R[z]+R[y]-R[z]; vcu[x][1].push_back(R[x]); vcu[z][0].push_back(R[x]); if(y==z) continue; vcd[y][1].push_back(maxn+len-R[y]); vcd[tp][0].push_back(maxn+len-R[y]); } } int ans[maxn]; void dfs(int x,int fa) { int nup=w[x]+R[x],nd=w[x]-R[x]+maxn; int prup=up[nup],prd=dn[nd]; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { dfs(to[i],x); } for(int i=0;i<vcu[x][1].size();i++) { int tp=vcu[x][1][i]; up[tp]++; } for(int i=0;i<vcd[x][1].size();i++) { int tp=vcd[x][1][i]; dn[tp]++; } ans[x]+=(up[nup]-prup); ans[x]+=(dn[nd]-prd); for(int i=0;i<vcu[x][0].size();i++) { int tp=vcu[x][0][i]; up[tp]--; } for(int i=0;i<vcd[x][0].size();i++) { int tp=vcd[x][0][i]; dn[tp]--; } } void work() { dfs(1,0); for(int i=1;i<n;i++) printf("%d ",ans[i]); printf("%d\n",ans[n]); } int main() { init(); work(); return 0; }
34.换教室
期望dp.
dp[i][j][k]表示第i节课为止,共申请了j次,本次申请或不申请的期望。
然后就很好转移了,很好想但是写起来比较恶心,十分难看。
神奇的是数据两个教室之间会用很多条路你要取最小的。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> const int maxv=299+5; const int maxn=2005; using namespace std; typedef long long LL; int n,m,v,e,a[maxn],b[maxn]; double c[maxn],dp[maxn][maxn][2],d[maxv][maxv]; void init() { scanf("%d%d%d%d",&n,&m,&v,&e); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) scanf("%d",&b[i]); for(int i=1;i<=n;i++) scanf("%lf",&c[i]); memset(d,127,sizeof(d)); for(int i=1;i<=e;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); d[x][y]=min(d[x][y],(double)z); d[y][x]=d[x][y]; } } void floyd() { for(int k=1;k<=v;k++) for(int i=1;i<=v;i++) for(int j=1;j<=v;j++) { if(i==j) d[i][j]=0; d[i][j]=min(d[i][j],d[i][k]+d[k][j]); } } void work() { floyd(); memset(dp,127,sizeof(dp)); dp[1][0][0]=0; dp[1][1][1]=0; for(int i=2;i<=n;i++) { int up=min(i,m); for(int j=0;j<=up;j++) { dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][0]+d[a[i-1]][a[i]]); if(j) { dp[i][j][0]=min(dp[i][j][0],dp[i-1][j][1]+ d[a[i-1]][a[i]]*(1-c[i-1])+d[b[i-1]][a[i]]*c[i-1]); dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][0]+ c[i]*d[a[i-1]][b[i]]+(1-c[i])*d[a[i-1]][a[i]]); } if(j>=2) dp[i][j][1]=min(dp[i][j][1],dp[i-1][j-1][1]+c[i-1]*c[i]*d[b[i-1]][b[i]] +c[i-1]*(1-c[i])*d[b[i-1]][a[i]]+(1-c[i-1])*c[i]*d[a[i-1]][b[i]] +(1-c[i-1])*(1-c[i])*d[a[i-1]][a[i]]); } } double ans=1e9; for(int i=0;i<=m;i++) { ans=min(ans,dp[n][i][1]); ans=min(ans,dp[n][i][0]); } printf("%.2lf\n",ans); } int main() { init(); work(); return 0; }
35.双栈排序
发现两个元素$i, j$不能放进同一个栈当且仅当存在$k$使得$i<j<k,a_k<a_i<a_j$
将这样i,j之间连边,整个图跑一遍二分图染色。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> #include<stack> const int maxn=1005; typedef long long LL; using namespace std; int n,a[maxn],col[maxn],ok[maxn],mi[maxn],fl; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; } void init() { read(n); for(int i=1;i<=n;i++) read(a[i]); } int ecnt,fir[maxn],nxt[maxn*maxn],to[maxn*maxn]; void add(int u,int v) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; } void dfs(int x) { for(int i=fir[x];i;i=nxt[i]) { if(col[to[i]]!=-1) { if(col[to[i]]==col[x]) fl=1; return; } else { col[to[i]]=col[x]^1; dfs(to[i]); } } } stack<int>s1; stack<int>s2; void work() { memset(col,-1,sizeof(col)); mi[n]=a[n]; for(int i=n-1;i>=1;i--) mi[i]=min(mi[i+1],a[i]); for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) if(a[j]>a[i]&&(j+1<=n&&mi[j+1]<a[i])) add(i,j); for(int i=1;i<=n;i++) if(col[i]==-1) { col[i]=1; dfs(i); if(fl) break; } if(fl) { printf("0\n"); return; } int now=0; for(int i=1;i<=n;i++) { int x=s1.empty()?0:s1.top(); int y=s2.empty()?0:s2.top(); while(x==now+1) { s1.pop(); x=s1.empty()?0:s1.top(); now++; printf("b "); } if(col[i]==1) { s1.push(a[i]); printf("a "); } else { while(y==now+1) { s2.pop(); y=s2.empty()?0:s2.top(); now++; printf("d "); } s2.push(a[i]); printf("c "); } } while(!s1.empty()||!s2.empty()) { int x=s1.empty()?0:s1.top(); int y=s2.empty()?0:s2.top(); if(x==now+1) { now++; printf("b "); s1.pop(); } else if(y==now+1) { now++; printf("d "); s2.pop(); } } printf("\n"); } int main() { init(); work(); return 0; }
36.组合数问题
这种水题去年的我竟然做不来?太弱啦,怪不得当时学长感觉很无语23333
n^2预处理出组合数和答案,O(1)询问即可。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> typedef long long LL; using namespace std; int n,m,k,t,C[2005][2005],ans[2005][2005]; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; } void pre() { for(int i=0;i<=2000;i++) C[i][0]=1; for(int i=1;i<=2000;i++) { for(int j=1;j<=2000;j++) { if(j<=i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%k; ans[i][j]=ans[i][j-1]; if(j<=i&&!C[i][j]) ans[i][j]++; } } for(int i=1;i<=2000;i++) for(int j=1;j<=2000;j++) ans[i][j]+=ans[i-1][j]; } void init() { read(t); read(k); pre(); for(int i=1;i<=t;i++) { read(n); read(m); m=min(n,m); printf("%d\n",ans[n][m]); } } int main() { init(); return 0; }
37.子串
dp.比较好想,dp[i][j][k]表示当前匹配到b的第i位,已经用了j个串,当且位匹配或者不匹配的方案数。
注意,开滚动数组转移,不合法的状态要赋为0。然后上一位是匹配上的这一位继续匹配的话可以不开也可以重开一段。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> typedef long long LL; using namespace std; const int maxn=1005; const int maxm=205; int n,m,kk; const int mod=1e9+7; char a[maxn],b[maxm]; LL dp[2][maxm][maxm][2]; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; } void init() { read(n); read(m); read(kk); scanf("%s",a+1); scanf("%s",b+1); } void work() { int o=0; LL ans=0; dp[o][0][0][0]=1; for(int i=1;i<=n;i++) { o^=1; int up=min(i,m); for(int j=0;j<=m;j++) for(int k=0;k<=j;k++) { dp[o][j][k][0]=(dp[o^1][j][k][0]+dp[o^1][j][k][1])%mod; if(a[i]==b[j]) { dp[o][j][k][1]=dp[o^1][j-1][k][1]; if(k) (dp[o][j][k][1]+=(dp[o^1][j-1][k-1][0]+dp[o^1][j-1][k-1][1])%mod)%mod; } else dp[o][j][k][1]=0; if(k==kk&&j==m) (ans+=dp[o][j][k][1])%=mod; } } printf("%lld\n",ans); } int main() { init(); work(); return 0; }
38.蚯蚓
三个队列维护。
注意要开LL,然后开了LL以后极大值要有那么大。。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> #define INF 1e18 typedef long long LL; using namespace std; const int maxm=7e6+5; const int maxn=1e5+5; int n,m,q,u,v,t; LL a[maxn]; queue<LL>que[3]; void read(LL &ret) { char ch=getchar(); ret=0; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; } bool cmp(const int &A,const int &B) { return A>B; } void init() { scanf("%d%d%d%d%d%d",&n,&m,&q,&u,&v,&t); for(int i=1;i<=n;i++) read(a[i]); } LL ck(int i) { return que[i].empty()?-INF:que[i].front(); } void work() { sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) que[0].push(a[i]); for(int ti=1;ti<=m;ti++) { LL a=ck(0),b=ck(1),c=ck(2); LL mx=max(max(a,b),c); if(mx==a) que[0].pop(); else if(mx==b) que[1].pop(); else que[2].pop(); mx+=(LL)(ti-1)*q; if(ti%t==0) printf("%lld ",mx); LL ll=((double)u/v)*mx,rr=mx-ll; if(ll<rr) swap(ll,rr); que[1].push(ll-ti*q); que[2].push(rr-ti*q); } printf("\n"); int sz=0; for(;;) { LL a=ck(0),b=ck(1),c=ck(2); LL mx=max(max(a,b),c); if(mx==a) que[0].pop(); else if(mx==b) que[1].pop(); else que[2].pop(); mx+=(LL)m*q; ++sz; if(sz%t==0) printf("%lld ",mx); if(sz==n+m) break; } printf("\n"); } int main() { init(); work(); return 0; }
39. 解方程
对质数p取模,若数i不合法则i+p也不合法。多取几个模一模,把不合法的筛掉。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> typedef long long LL; using namespace std; const int maxn=1000005; LL n,m,a[150][5],p[5]={10007,10917,30071,12007,65537},no[maxn]; void read(LL ret[]) { char ch=getchar(); int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) { for(int i=0;i<5;i++) ret[i]=(ret[i]*10+ch-'0')%p[i]; } if(f==-1) { for(int i=0;i<5;i++) ret[i]=p[i]-ret[i]; } } void init() { scanf("%d%d",&n,&m); for(int i=1;i<=n+1;i++) read(a[i]); } int ok(int x,int o) { LL sum=0,xx=x; for(int i=n+1;i>=1;i--) { sum=((sum+a[i][o])*xx)%p[o]; } return sum==0; } void work() { for(int o=0;o<5;o++) { for(int i=1;i<=p[o];i++) { if(!ok(i,o)) { for(int j=i;j<=m;j+=p[o]) { no[j]=1; } } } } int ans=0; for(int i=1;i<=m;i++) if(!no[i]) ans++; printf("%d\n",ans); for(int i=1;i<=m;i++) if(!no[i]) printf("%d\n",i); } int main() { init(); work(); return 0; }
40.运输计划
又一道树上差分。二分+树上差分。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> const int maxn=300005; typedef long long LL; using namespace std; int n,m; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; } int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1]; void add(int u,int v,int w) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w; } int x[maxn],y[maxn],z[maxn],d[maxn]; void init() { read(n); read(m); for(int i=1;i<n;i++) { int u,v,w; read(u); read(v); read(w); add(u,v,w); } for(int i=1;i<=m;i++) { read(x[i]); read(y[i]); } } int R[maxn],f[maxn][20],dis[maxn]; void dfs(int x,int fa) { f[x][0]=fa; R[x]=R[fa]+1; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { dis[to[i]]=dis[x]+val[i]; dfs(to[i],x); } } void make_st() { for(int i=1;i<=19;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; } int get_lca(int a,int b) { if(R[a]<R[b]) swap(a,b); for(int i=19;i>=0;i--) if(f[a][i]&&R[f[a][i]]>=R[b]) a=f[a][i]; if(a==b) return a; for(int i=19;i>=0;i--) if(f[a][i]&&f[a][i]!=f[b][i]) a=f[a][i],b=f[b][i]; return f[a][0]; } int g[maxn],cnt,mxx; int DFS(int x,int fa,int pr,int mid) { for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { if(DFS(to[i],x,i,mid)) return 1; g[x]+=g[to[i]]; } if(g[x]==cnt&&mxx-val[pr]<=mid) return 1; return 0; } int check(int mid) { cnt=0; mxx=0; memset(g,0,sizeof(g)); for(int i=1;i<=n;i++) { if(d[i]>mid) { cnt++; g[x[i]]++; g[y[i]]++; g[z[i]]-=2; mxx=max(mxx,d[i]); } } return DFS(1,0,0,mid); } void work() { dfs(1,0); make_st(); for(int i=1;i<=n;i++) { z[i]=get_lca(x[i],y[i]); d[i]=dis[x[i]]+dis[y[i]]-2*dis[z[i]]; } int l=0,r=3e8,ans; while(l<=r) { int mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%d\n",ans); } int main() { init(); work(); return 0; }
41.开车旅行
把高度排序连成链表,从1到n看每个人左右四个,它的最大和次大一定在其中,然后把它从链表中删掉保证现在要找的人左右都在它后面。
然后一轮开车为a跳到它的次大b再跳到它的最大,做倍增。
感觉挺好写的然后犯了一大堆智障错误,调到死亡。。。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> #define INF 0xfffffff const int maxn=100005; typedef long long LL; using namespace std; int n,m,h[maxn],a[maxn],zd[maxn],cd[maxn],x0,s[maxn],lim[maxn]; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; ret*=f; } bool cmp(const int &A,const int &B) { return h[A]<h[B]; } void init() { read(n); for(int i=1;i<=n;i++) { a[i]=i; read(h[i]); } read(x0); read(m); for(int i=1;i<=m;i++) { read(s[i]); read(lim[i]); } } int p[maxn][20]; LL da[maxn][20],db[maxn][20]; void make_st() { for(int i=1;i<=n;i++) { p[i][0]=zd[cd[i]]; da[i][0]=abs(h[cd[i]]-h[i]); db[i][0]=abs(h[zd[cd[i]]]-h[cd[i]]); } for(int i=1;i<=19;i++) for(int j=1;j<=n;j++) { p[j][i]=p[p[j][i-1]][i-1]; da[j][i]=da[j][i-1]+da[p[j][i-1]][i-1]; db[j][i]=db[j][i-1]+db[p[j][i-1]][i-1]; } } void cal(int st,int limit,LL &az,LL &bz) { int now=st; az=0; bz=0; for(int i=19;i>=0;i--) { if(p[now][i]&&az+bz+da[now][i]+db[now][i]<=limit) { az+=da[now][i]; bz+=db[now][i]; now=p[now][i]; } } if(cd[now]&&az+bz+abs(h[cd[now]]-h[now])<=limit) az+=abs(h[cd[now]]-h[now]); } void ck(int i,int x,int &a,int &b) { if(!i) return; if(!a) a=i; else if(!b) { b=i; if(abs(h[a]-h[x])>abs(h[b]-h[x])) swap(a,b); } else { if(abs(h[i]-h[x])<abs(h[a]-h[x])) b=a,a=i; else if(abs(h[i]-h[x])<abs(h[b]-h[x])) b=i; } } int pr[maxn],nx[maxn]; void work() { sort(a+1,a+n+1,cmp); for(int i=1;i<=n;i++) { pr[a[i]]=a[i-1]; nx[a[i]]=a[i+1]; } for(int i=1;i<=n;i++) { int aa=0,bb=0; ck(pr[i],i,aa,bb); ck(pr[pr[i]],i,aa,bb); ck(nx[i],i,aa,bb); ck(nx[nx[i]],i,aa,bb); if(nx[i]) pr[nx[i]]=pr[i]; if(pr[i]) nx[pr[i]]=nx[i]; zd[i]=aa; cd[i]=bb; } make_st(); int ans=0,ok=0; double now=INF; for(int i=1;i<=n;i++) { LL a,b; cal(i,x0,a,b); if(!ok||(double)a/b<now) { ans=i; ok=1; now=(double)a/b; } } printf("%d\n",ans); for(int i=1;i<=m;i++) { LL a,b; cal(s[i],lim[i],a,b); printf("%lld %lld\n",a,b); } } int main() { init(); work(); return 0; }
42.愤怒的小鸟
其实去年考完Noip不久就写了这道题,不过当时觉得还是很难,而且几乎照着某个题解抄了一遍后来自己根本没有印象,就又写了一下。
然后发现其实蛮简单的,而且感觉去年d2比d1简单得多呀,
d1t1不说了签到题,t2,一道史无前例的神题,t3又考了期望,难得一比,相比d2t1水题,t2暴力有70~85分,t3水状压,多么亲切。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> #define eps 1e-10 typedef long long LL; using namespace std; const int maxn=20; int n,m,tot,z[500],dp[1<<19]; double x[maxn],y[maxn]; void cal(int u,int v) { if(x[u]==x[v]) return ; double b=(y[u]*x[v]*x[v]-y[v]*x[u]*x[u])/(x[u]*x[v]*x[v]-x[v]*x[u]*x[u]); double a=(y[u]-b*x[u])/(x[u]*x[u]); if(a>0) return ; z[++tot]|=(1<<u-1); z[tot]|=(1<<v-1); for(int k=v+1;k<=n;k++) if(fabs(x[k]*x[k]*a+x[k]*b-y[k])<=eps) z[tot]|=(1<<k-1); } void work() { tot=0; memset(dp,127,sizeof(dp)); dp[0]=0; for(int i=1;i<=n;i++) for(int j=i+1;j<=n;j++) cal(i,j); for(int i=1;i<=n;i++) z[++tot]=(1<<i-1); int nn=(1<<n)-1; for(int j=0;j<=nn;j++) for(int i=1;i<=tot;i++) if((j|z[i])>j) dp[j|z[i]]=min(dp[j|z[i]],dp[j]+1); printf("%d\n",dp[nn]); for(int i=1;i<=tot;i++) z[i]=0; } void init() { int T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]); work(); } } int main() { init(); return 0; }
43.疫情控制
二分+树上贪心。下次是不是该出一道二分+树上dp了。
De了超级久的Bug,然后今天洛谷还各种炸。。。
不得不说codevs真是,,直到洛谷活过来让我A了都没有测出来
题解:
贪心:若一个军队没有到根,那么它越往上走越优。
二分一个答案,每个军队倍增地往上跳,若是在答案范围内跳不到根,就把能跳到的最高点占领。
若是能跳到根还能跳,就存下来,方便别人用。
dfs一遍把已经占领的子树确定下来,然后处理没有被完全占领的子树。这些地方一定是在跟的儿子那里被占领。
贪心:询问没有被完全占领的儿子,若是它这里有一个还能跳的距离小于它到根的就直接由它来占领自己,可以用个堆维护。(记得清0)
(自己的不一定帮自己,存在自己的儿子中的军队去帮助别人再让另一个人来帮助自己的情况)
剩下的儿子到根的距离存下来,排序,和还能跳的数组排序,贪心地从最大的开始比较。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> typedef long long LL; using namespace std; const int maxn=50005; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; ret*=f; } int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1]; void add(int u,int v,int w) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w; } LL sum; int n,m,is[maxn],sz; void init() { read(n); for(int i=1;i<n;i++) { int u,v,w; read(u); read(v); read(w); add(u,v,w); sum+=w; } read(m); for(int i=1;i<=m;i++) { int x; read(x); is[++sz]=x; } } int f[maxn][20]; LL d[maxn][20]; void dfs(int x,int fa) { f[x][0]=fa; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { d[to[i]][0]=val[i]; dfs(to[i],x); } } void make_st() { for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) { f[i][j]=f[f[i][j-1]][j-1]; d[i][j]=d[i][j-1]+d[f[i][j-1]][j-1]; } } int ok[maxn]; int DFS(int x,int fa) { int fl=1; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { if(ok[to[i]]) continue; if(!DFS(to[i],x)) fl=0; else ok[to[i]]=1; } if(fl==0||!nxt[fir[x]]) return 0; return 1; } bool cmp(const int &A,const int &B) { return A>B; } priority_queue<int,vector<int>,greater<int> >que[maxn]; int a[maxn],b[maxn],tota,totb; int check(LL mid) { if(mid==1) { int debug=1; } memset(ok,0,sizeof(ok)); memset(a,0,sizeof(a)); memset(b,0,sizeof(b)); tota=totb=0; for(int i=fir[1];i;i=nxt[i]) { int y=to[i]; while(!que[y].empty() ) { que[y].pop(); } } for(int i=1;i<=sz;i++) { int x=is[i]; if(x==1) a[++tota]=mid; LL now=0; for(int j=19;j>=0;j--) if(f[x][j]&&f[x][j]!=1&&d[x][j]+now<=mid) { now+=d[x][j]; x=f[x][j]; } if(f[x][0]==1&&now+d[x][0]<mid) { que[x].push(mid-(now+d[x][0])); } else ok[x]=1; } if(ok[1]) return 1; if(DFS(1,0)) return 1; for(int i=fir[1];i;i=nxt[i]) { int y=to[i]; if(!ok[y]) { if(!que[y].empty() ) { int z=que[y].top(); if(z<val[i]) { que[y].pop(); ok[y]=1; } else b[++totb]=val[i]; } else b[++totb]=val[i]; } while(!que[y].empty()) { int z=que[y].top(); a[++tota]=z; que[y].pop(); } } if(!totb) return 1; sort(a+1,a+tota+1,cmp); sort(b+1,b+totb+1,cmp); for(int i=1;i<=totb;i++) if(a[i]<b[i]) return 0; return 1; } void work() { dfs(1,0); make_st(); LL l=0,r=sum,ans=-1; while(l<=r) { LL mid=(l+r)>>1; if(check(mid)) ans=mid,r=mid-1; else l=mid+1; } printf("%lld\n",ans); } int main() { init(); work(); return 0; }
44.飞扬的小鸟
debug到怀疑人生系列
背包,往下掉是01背包,往上走是完全背包。
几个坑点,dp[i-1][m]可以转移到dp[i][m](到了顶继续往上还是在顶上)
向上飞转移的时候只判断从i-1转移来的状态要合法,i转移来的状态不一定合法(连续跳多次其实是一次性跳上去的,中途的某个位置不一定合法)
先跑往上跳的再跑往下掉的(不能从掉来的位置往上跳)。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<queue> #include<cmath> #include<ctime> #define INF 0xfffffff typedef long long LL; using namespace std; const int maxn=10005; int n,m,kk; int up[maxn],dn[maxn],l[maxn],h[maxn]; void read(int &ret) { char ch=getchar(); ret=0;int f=1; while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; ret*=f; } void init() { read(n); read(m); read(kk); for(int i=1;i<=n;i++) { read(up[i]); read(dn[i]); l[i]=0; h[i]=m+1; } l[0]=0; h[0]=m+1; for(int i=1;i<=kk;i++) { int x,ll,r; read(x); read(ll); read(r); l[x]=ll; h[x]=r; } } int ok(int i,int j) { return j>l[i]&&j<h[i]; } int dp[maxn][1005],now; void work() { for(int i=1;i<maxn;i++) for(int j=1;j<=1000;j++) dp[i][j]=INF; for(int i=1;i<=m;i++) dp[0][i]=0; int fl=1,as,ans=1e9; for(int i=1;i<=n;i++) { fl=0; for(int j=1;j<=m;j++) { if(j>up[i]) { if(ok(i-1,j-up[i])) dp[i][j]=min(dp[i][j],dp[i-1][j-up[i]]+1); dp[i][j]=min(dp[i][j],dp[i][j-up[i]]+1); } if(j==m) { for(int k=up[i];k>=0;k--) { if(ok(i-1,j-k)) dp[i][j]=min(dp[i][j],dp[i-1][j-k]+1); dp[i][j]=min(dp[i][j],dp[i][j-k]+1); } } } for(int j=l[i]+1;j<=h[i]-1;j++) { if(j+dn[i]<=m&&ok(i,j)&&ok(i-1,j+dn[i])) { dp[i][j]=min(dp[i][j],dp[i-1][j+dn[i]]); } if(i==n) ans=min(ans,dp[i][j]); if(dp[i][j]!=INF) fl=1; } if(!fl) break; if(l[i]!=0||h[i]!=m+1) now++; } if(!fl) printf("0\n%d\n",now); else { printf("1\n"); printf("%d\n",ans); } } int main() { init(); work(); return 0; }
45.积木大赛
差分,相当于在每个位置加或者减多少,一个加可以与一个减配对,于是把大于0的部分和小于0的部分分别绝对值加起来取max就是答案。
//Twenty #include<algorithm> #include<iostream> #include<cstdlib> #include<cstring> #include<cstdio> #include<cmath> #include<ctime> #include<queue> const int maxn=100005; using namespace std; typedef long long LL; int n,a[maxn],b[maxn],aa,bb; void read(int &x) { int ret=0,f=1; char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) ret=ret*10+ch-'0'; x=ret*f; } void init() { read(n); for(int i=1;i<=n;i++) { read(a[i]); b[i]=a[i]-a[i-1]; if(b[i]>0) aa+=b[i]; else bb-=b[i]; } aa=max(aa,bb); printf("%d\n",aa); } int main() { init(); return 0; }
46.树网的核
乍一看很难,然而数据范围表示是一道水题。
对于每个点进行一次dfs求出它到其他点的距离,这个同时求出树的直径(从某点dfs到最远点,该点再dfs到最远点);
然后直径暴力跑就可过。何况数据水得一比。
//Twenty #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<vector> #include<cmath> #include<queue> typedef long long LL; using namespace std; const int maxn=305; int n,s,x,y,z,d[maxn][maxn],que[maxn],ql=1,qr; void read(int &x) { x=0; int f=1; char ch=getchar(); while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar(); if(ch=='-') f=-1,ch=getchar(); for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0'; x*=f; } int ecnt,fir[maxn],nxt[maxn<<1],to[maxn<<1],val[maxn<<1],f[maxn]; void add(int u,int v,int w) { nxt[++ecnt]=fir[u]; fir[u]=ecnt; to[ecnt]=v; val[ecnt]=w; nxt[++ecnt]=fir[v]; fir[v]=ecnt; to[ecnt]=u; val[ecnt]=w; } void init() { read(n); read(s); for(int i=1;i<n;i++) { read(x); read(y); read(z); add(x,y,z); } } void dfs(int x,int fa,int top,int o) { if(o) f[x]=fa; for(int i=fir[x];i;i=nxt[i]) if(to[i]!=fa) { d[to[i]][top]=d[x][top]+val[i]; d[top][to[i]]=d[to[i]][top]; dfs(to[i],x,top,o); } } int ans=1e9; void ck(int a,int b) { int as=0; for(int i=1;i<=n;i++) { int cc=min(d[a][i],d[b][i]); for(int j=ql;j<=qr;j++) cc=min(cc,d[que[j]][i]); as=max(as,cc); } ans=min(ans,as); } void cal(int xx,int yy,int lim) { int s=yy,t=xx,l=s,r=s,now=0; while(r!=t&&now+d[r][f[r]]<=lim) { now+=d[r][f[r]]; if(r!=s) que[++qr]=r; r=f[r]; } ck(l,r); while(r!=t) { now-=d[l][f[l]]; l=f[l]; ql++; int pr=r; while(r!=t&&now+d[r][f[r]]<=lim) { now+=d[r][f[r]]; if(r!=pr) que[++qr]=r; r=f[r]; } ck(l,r); } printf("%d\n",ans); } void work() { int xx=0,yy=0; for(int i=1;i<=n;i++) { if(i==xx) { dfs(i,0,i,1); for(int j=1;j<=n;j++) { if(!yy||d[i][j]>d[i][yy]) yy=j; } } else dfs(i,0,i,0); if(i==1) { for(int j=2;j<=n;j++) { if(!xx||d[i][j]>d[i][xx]) xx=j; } } } cal(xx,yy,s); } int main() { init(); work(); return 0; }
----------------------------------------------------------------------------------------------------------------------------------------
原计划上周四的样子就要结束的跟着洛谷刷noip题被我拖到了现在。。去掉纯粹的入门难度水题大概就35道的样子,然而我写得并不算多顺利,足见我有弱QAQ
对noip题多少有一定感觉了吧,虽然听说今年会变难。
还有17天就noip了,一年带点的竞赛生涯大概会就此结束了,毕竟比起创造奇迹的人,世界上还是没能创造奇迹的人多得多嘛(虽然我相信水团会创造自己的奇迹)。
之前在知乎上看到一个回答,小时候美少女战士非常火,小朋友们一起玩角色扮演的时候,特别羡慕那些选粉色的女孩子,大家都是第一次出生在这个世界上,怎么就有勇气选最好的呢。
其实我自己的话以前也很难做到,后来很努力地想试一试,后来每次都失败了呀。
我从小到大最喜欢的两件事情都是呀,
累败累战,累战累败吧。
不知道我在说些什么。
现在手机欠费了,不能天天去看竞赛退役是怎么样的体验了。