8.2/baltic神(水)题
summary:10
bzoj1334:
Description
N个政党要组成一个联合内阁,每个党都有自己的席位数. 现在希望你找出一种方案,你选中的党的席位数要大于总数的一半,并且联合内阁的席位数越多越好. 对于一个联合内阁,如果某个政党退出后,其它党的席位仍大于总数的一半,则这个政党被称为是多余的,这是不允许的.
Input
第一行给出有多少个政党.其值小于等于300 下面给出每个政党的席位数.总席位数小于等于 100000
Output
你的组阁方案中最多能占多少个席位.
背包dp。一开始想着贪心贪心。。。后来在yyl的引导下想了dp。发现一半还是没办法搞啊。最后在yyl的引导下走上了正途。。。感动。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } int f[100005]; int a[100005]; int main(){ int n=read(),sum=0,ans=-1; rep(i,1,n) a[i]=read(),sum+=a[i]; sort(a+1,a+n+1); f[0]=1; dwn(i,n,1) dwn(j,sum/2+a[i],a[i]) if(f[j-a[i]]) f[j]=1,ans=max(ans,j); printf("%d\n",ans); return 0; }
bzoj1339:
Description
匪徒准备从一个车站转移毒品到另一个车站,警方准备进行布控. 对于每个车站进行布控都需要一定的代价,现在警方希望使用最小的代价控制一些车站,使得去掉这些车站后,匪徒无法从原定的初始点到达目标点
Input
第一行输入N,M代表车站的总个数,及有多少条双向边连接它们. 2<=n<=200 , 1 <=m<=20000. 第二行给出两个数a,b,代表匪徒的出发点及目标点.1<=a,b<=N,a<>b. 再下来有N行,给出对第i个车站进行布控所需要的Money,其不超过10 000 000 再下来M行,用于描述图的结构.
Output
最少需要多少Money
嗯裸最小割拆点就好了。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=405; const int maxn=1e5+5; const int inf=0x7f7f7f7f; struct edge{ int to,cap;edge *next,*rev; }; edge edges[maxn],*pt=edges,*head[nmax],*cur[nmax],*p[nmax]; int cnt[nmax],h[nmax]; void add(int u,int v,int d){ pt->to=v;pt->cap=d;pt->next=head[u];head[u]=pt++; } void adde(int u,int v,int d){ add(u,v,d);add(v,u,0);head[u]->rev=head[v];head[v]->rev=head[u]; } int maxflow(int s,int t,int n){ clr(cnt,0);cnt[0]=n;clr(h,0); int flow=0,a=inf,x=s;edge *e; while(h[s]<n){ for(e=cur[x];e;e=e->next) if(e->cap>0&&h[e->to]+1==h[x]) break; if(e){ p[e->to]=cur[x]=e;a=min(a,e->cap);x=e->to; if(x==t){ while(x!=s) p[x]->cap-=a,p[x]->rev->cap+=a,x=p[x]->rev->to; flow+=a,a=inf; } }else{ if(!--cnt[h[x]]) break; h[x]=n; for(e=head[x];e;e=e->next) if(e->cap>0&&h[x]>h[e->to]+1) cur[x]=e,h[x]=h[e->to]+1; cnt[h[x]]++; if(x!=s) x=p[x]->rev->to; } } return flow; } int main(){ int n=read(),m=read(),s=read(),t=read();t+=n; rep(i,1,n) adde(i,i+n,read()); rep(i,1,m){ int u=read(),v=read(); adde(u+n,v,inf);adde(v+n,u,inf); } printf("%d\n",maxflow(s,t,n+n)); return 0; }
bzoj1342:
Description
静音问题 数字录音中,声音是用表示空气压力的数字序列描述的,序列中的每个值称为一个采样,每个采样之间间隔一定的时间。 很多声音处理任务都需要将录到的声音分成由静音隔开的几段非静音段。为了避免分成过多或者过少的非静音段,静音通常是这样定义的:m个采样的序列,该序列中采样的最大值和最小值之差不超过一个特定的阈值c。 请你写一个程序,检测n个采样中的静音。
Input
第一行有三个整数n,m,c( 1<= n<=1000000,1<=m<=10000, 0<=c<=10000),分别表示总的采样数、静音的长度和静音中允许的最大噪音程度。第2行n个整数ai (0 <= ai <= 1,000,000),表示声音的每个采样值,每两个整数之间用空格隔开。
Output
列出了所有静音的起始位置i(i满足max(a[i, . . . , i+m−1]) − min(a[i, . . . , i+m−1]) <= c),每行表示一段静音的起始位置,按照出现的先后顺序输出。如果没有静音则输出NONE。
我想到好像是o(n)的做法,维护最大最小次大次小应该可以,但是很麻烦。。。然后发现用单调队列维护就可以了。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } char num[7]; void print(int x){ int len=0; while(x){ num[++len]=x%10+'0';x/=10; } dwn(i,len,1) putchar(num[i]);putchar('\n'); } const int nmax=1000005; int a[nmax],q[nmax],Q[nmax]; int main(){ int n=read(),m=read(),c=read(); rep(i,1,n) a[i]=read(); int s=1,t=1,l=1,r=1;q[1]=1,Q[1]=1; bool flag=false; rep(i,2,n){ while(s<=t&&q[s]+m<=i) s++; while(s<=t&&a[q[t]]<=a[i]) t--; q[++t]=i; while(l<=r&&Q[l]+m<=i) l++; while(l<=r&&a[Q[r]]>=a[i]) r--; Q[++r]=i; if(a[q[s]]-a[Q[l]]<=c&&i>=m) flag=true,print(i-m+1); // printf("%d:%d %d\n",i,q[s],Q[l]); } if(!flag) printf("NONE\n"); return 0; }
bzoj1349:
Description
Write a program to calculate integer square roots.
Input
The input is read from a text file named squint.in. Its only line consists of an integer 0 < = n < 2^63 .
Output
Its only line consists of the smallest nonnegative integer q such that q^2 >= n .
QAQ。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<cmath> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define ll long long int main(){ ll a;scanf("%lld",&a); ll tmp=(ll)sqrt(a); if(tmp*tmp==a) { printf("%lld\n",tmp); }else printf("%lld\n",tmp+1); return 0; }
bzoj1355:
Description
给你一个字符串,它是由某个字符串不断自我连接形成的。 但是这个字符串是不确定的,现在只想知道它的最短长度是多少.
Input
第一行给出字符串的长度,1 < L ≤ 1,000,000. 第二行给出一个字符串,全由小写字母组成.
Output
输出最短的长度
我作死想用ac自动机水过去。然而空间炸了。。。但还是把ac自动机的放一下吧。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) const int nmax=1000006; char s[nmax]; int ch[nmax][26],fail[nmax],F[nmax],pt=0; void insert(){ int len=strlen(s),t=0; rep(i,0,len-1) { if(!ch[t][s[i]-'a']) ch[t][s[i]-'a']=++pt; t=ch[t][s[i]-'a']; F[t]=i+1; } } queue<int>q;int n; void getfail(){ q.push(0);fail[0]=0; while(!q.empty()){ int x=q.front();q.pop(); rep(i,0,25){ if(ch[x][i]) q.push(ch[x][i]),fail[ch[x][i]]=x==0?0:ch[fail[x]][i]; else ch[x][i]=x==0?0:ch[fail[x]][i]; } } printf("%d\n",n-F[fail[n]]); } int mian(){ scanf("%d",&n);scanf("%s",s);insert();getfail(); return 0; }
bzoj1369:
Description
给出一棵树,要求你为树上的结点标上权值,权值可以是任意的正整数 唯一的限制条件是相临的两个结点不能标上相同的权值,要求一种方案,使得整棵树的总价值最小。
Input
先给出一个数字N,代表树上有N个点,N<=10000 下面N-1行,代表两个点相连
Output
最小的总权值
开始以为是1和2就可以了,交上去WA了。然后想到了反例。树形dp。一定要想仔细啊。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=10005; const int inf=0x7f7f7f7f; struct edge{ int to;edge *next; }; edge edges[nmax<<1],*pt=edges,*head[nmax]; int dp[nmax][4]; void add(int u,int v){ pt->to=v;pt->next=head[u];head[u]=pt++; pt->to=u;pt->next=head[v];head[v]=pt++; } void dfs(int x,int fa){ qwq(x) if(o->to!=fa){ dfs(o->to,x); rep(i,1,3){ int ans=inf; rep(j,1,3) if(i!=j) ans=min(ans,dp[o->to][j]); dp[x][i]+=ans; } } rep(i,1,3) dp[x][i]+=i; } int main(){ int n=read(); rep(i,1,n-1){ int u=read(),v=read();add(u,v); } dfs(1,0); int ans=inf; rep(i,1,3) ans=min(ans,dp[1][i]); printf("%d\n",ans); return 0; }
bzoj1370:
Description
在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足: 1、 我朋友的朋友是我的朋友; 2、 我敌人的敌人是我的朋友; 所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙?
Input
第1行为n和m,N小于1000,M小于5000; 以下m行,每行为p x y,p的值为0或1,p为0时,表示x和y是朋友,p为1时,表示x和y是敌人。
Output
一个整数,表示这n个人最多可能有几个团伙。
并查集。将每个人拆成两个点。然而AB朋友AC朋友BC敌人我不知道怎么处理。翻了题解居然是直接这样子就能过了?
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=2005; int fa[nmax]; char s[5]; int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); } int main(){ int n=read(),m=read(),u,v; rep(i,1,n+n) fa[i]=i; rep(i,1,m){ scanf("%s",s);u=read(),v=read(); if(s[0]=='F') fa[find(u)]=find(v); else fa[find(u+n)]=find(v),fa[find(v+n)]=find(u); } rep(i,1,n) find(i); sort(fa+1,fa+n+1); int ans=1; rep(i,2,n) if(fa[i]!=fa[i-1]) ans++; printf("%d\n",ans); return 0; }
bzoj1375:
Description
来越多,因此选择最佳路径是很现实的问题。城市的道路是双向的,每条道路有固定的旅行时间以及需要支付的费用。路径由连续的道路组成。总时间是各条道路旅行时间的和,总费用是各条道路所支付费用的总和。同样的出发地和目的地,如果路径A比路径B所需时间少且费用低,那么我们说路径A比路径B好。对于某条路径,如果没有其他路径比它好,那么该路径被称为最优双调路径。这样的路径可能不止一条,或者说根本不存在。 给出城市交通网的描述信息,起始点和终点城市,求最优双条路径的条数。城市不超过100个,边数不超过300,每条边上的费用和时间都不超过100。
Input
第一行给出有多少个点,多少条边,开始点及结束点. 下面的数据用于描述这个地图
Output
有多少条最优双调路径
%%%ccz大爷在其耐心的引导下我终于看懂了样例。。。不行不行,以后不能耽误神犇的时间了。。。
f[i][j]表示i点距离为j的最小费用。然后跑spfa就好了。完全不会。。。抄袭!
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #include<queue> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=105; const int maxn=605; const int inf=0x7f7f7f7f; struct edge{ int to,dist,cost;edge *next; }; edge edges[maxn],*pt=edges,*head[nmax]; bool inq[nmax][41000];int f[nmax][41000],n; void adde(int u,int v,int d,int w){ pt->to=v;pt->dist=d;pt->cost=w;pt->next=head[u],head[u]=pt++; pt->to=u;pt->dist=d;pt->cost=w;pt->next=head[v],head[v]=pt++; } struct node{ int u,d; node(int u,int d):u(u),d(d){} }; queue<node>q; void spfa(int s,int t){ clr(f,0x7f);f[s][0]=0;inq[s][0]=1;q.push(node(s,0)); while(!q.empty()){ node x=q.front();q.pop();inq[x.u][x.d]=0; if(x.d>n*nmax-n) continue; qwq(x.u) if(f[x.u][x.d]+o->cost<f[o->to][x.d+o->dist]){ f[o->to][x.d+o->dist]=f[x.u][x.d]+o->cost; if(!inq[o->to][x.d+o->dist]) { q.push(node(o->to,x.d+o->dist)); inq[o->to][x.d+o->dist]=1; } } } } int main(){ n=read();int m=read(),s=read(),t=read(),u,v,d,w,dsum=0,csum=0; rep(i,1,m) u=read(),v=read(),d=read(),w=read(),adde(u,v,d,w); spfa(s,t); int ans=0,nmin=inf; rep(i,0,n*nmax-n) { if(f[t][i]==inf) continue; if(f[t][i]>=nmin) continue; nmin=f[t][i];ans++; } printf("%d\n",ans); return 0; }
bzoj1391:
Description
有N个工作,M种机器,每种机器你可以租或者买过来. 每个工作包括若干道工序,每道工序需要某种机器来完成,你可以通过购买或租用机器来完成。 现在给出这些参数,求最大利润
Input
第一行给出 N,M(1<=N<=1200,1<=M<=1200) 下面将有N块数据,每块数据第一行给出完成这个任务能赚到的钱(其在[1,5000])及有多少道工序 接下来若干行每行两个数,分别描述完成工序所需要的机器编号及租用它的费用(其在[1,20000]) 最后M行,每行给出购买机器的费用(其在[1,20000])
Output
最大利润
看到题嘛这题肯定是最小割。肯定是最小割。。。YY了很久都不知道怎么处理租和买的关系。。。翻题解,“最小割保证ST不相连,即保证给所有工作分配了一种方案”。然后就懂了。。。妈呀我就不会换个思维吗。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) #define dwn(i,s,t) for(int i=s;i>=t;i--) #define clr(x,c) memset(x,c,sizeof(x)) #define qwq(x) for(edge *o=head[x];o;o=o->next) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } const int nmax=4005; const int maxn=3000005; const int inf=0x7f7f7f7f; struct edge{ int to,cap;edge *next,*rev; }; edge edges[maxn],*pt=edges,*head[nmax],*cur[nmax],*p[nmax]; int cnt[nmax],h[nmax]; void add(int u,int v,int d){ pt->to=v;pt->cap=d;pt->next=head[u];head[u]=pt++; } void adde(int u,int v,int d){ add(u,v,d);add(v,u,0);head[u]->rev=head[v];head[v]->rev=head[u]; } int maxflow(int s,int t,int n){ clr(cnt,0);clr(h,0);cnt[0]=n; int flow=0,a=inf,x=s;edge *e; while(h[s]<n){ for(e=cur[x];e;e=e->next) if(e->cap>0&&h[x]==h[e->to]+1) break; if(e){ cur[x]=p[e->to]=e;a=min(a,e->cap);x=e->to; if(x==t) { while(x!=s) p[x]->cap-=a,p[x]->rev->cap+=a,x=p[x]->rev->to; flow+=a,a=inf; } }else{ if(!--cnt[h[x]]) break; h[x]=n; for(e=head[x];e;e=e->next) if(e->cap>0&&h[x]>h[e->to]+1) h[x]=h[e->to]+1,cur[x]=e; cnt[h[x]]++; if(x!=s) x=p[x]->rev->to; } } return flow; } int main(){ int n=read(),m=read(),u,v,d,w,s=0,t=n+m+1,ans=0; rep(i,1,n){ u=read(),v=read(); rep(j,1,v) d=read(),w=read(),adde(i,d+n,w); adde(s,i,u);ans+=u; } rep(i,1,m) u=read(),adde(i+n,t,u); printf("%d\n",ans-maxflow(s,t,t+1)); return 0; }
bzoj1345:
Description
对于一个给定的序列a1, …, an,我们对它进行一个操作reduce(i),该操作将数列中的元素ai和ai+1用一个元素max(ai,ai+1)替代,这样得到一个比原来序列短的新序列。这一操作的代价是max(ai,ai+1)。进行n-1次该操作后,可以得到一个长度为1的序列。我们的任务是计算代价最小的reduce操作步骤,将给定的序列变成长度为1的序列。
Input
第一行为一个整数n( 1 <= n <= 1,000,000 ),表示给定序列的长度。接下来的n行,每行一个整数ai(0 <=ai<= 1, 000, 000, 000),为序列中的元素。
Output
只有一行,为一个整数,即将序列变成一个元素的最小代价。
超神的贪心。。。在YYL的引导下走向了正途。。。YYL:看这数据范围肯定是贪心。。。我:。。。
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define rep(i,s,t) for(int i=s;i<=t;i++) int read(){ int x=0;char c=getchar(); while(!isdigit(c)) c=getchar(); while(isdigit(c)) x=x*10+c-'0',c=getchar(); return x; } int a[1000005]; int main(){ int n=read();a[1]=read();long long ans=0; rep(i,2,n) a[i]=read(),ans+=max(a[i],a[i-1]); printf("%lld\n",ans); return 0; }
感动啊。。。被带着飞感动啊。。。