R311 DIV2 D. Vitaly and Cycle
飞机票:http://codeforces.com/contest/557/problem/D
题意:给你n个点和m条边的无向图,可能不是连接在一起的,并且没有子环和平行边,求最小添加几条边可以使它包含奇数点的圈,并且求满足这样方案的方法有多少种,两种方案不同即边的设置不完全一样。
分析:起先可以很容易知道最少的边不会超过3,即0,1,2,3,为什么?加入所有的点都不相连,即m等于0,那么肯定对任意三点添加三条边就可以有奇数点的圈,总方案就是C(n,3)即n*(n-1)*(n-2)/3;然后就可以用染色的方法对0,1,2进行判断,具体怎么判断,假设当前点为i,且i没有访问过,那么对i进行DFS,可以设i点的颜色为0,与i相连的点颜色为1,并且对i相连的没有访问过的边继续按照这个染色方法染色,即当前点的颜色为0,则相连的边为1,反之同理。假设当前的节点为now,它的邻边v被访问过,如果它们的颜色一样的话,就说明包含奇数点的圈,为什么?因为点是根据0,1,0,1染色的,因为两个相邻的颜色一样,说明,就是1,0,1,0,....1,因为没有最后一个点,它就是偶数个,包含了,它就是奇数个,只要是这样,就说明不用添加边,则为0条边,一种方法。反之的话,继续搜索,并且记录,染色为1的点的个数,和染色为0的点的个数。当搜索完的时候,得到为1的点为p,为0的点的数量为q,我们要使它包含奇数个点,所以我们要选择两个点,且它们之间的数量为奇数,因为我们知道只要两个点的颜色一样,则它们之间肯定包含了奇数个点的圈,所以我们从p里选择两个,即C(p,2)=p*(p-)/2,同理从q中选两个C(q,2)=q*(q-)/2,加到总方法里去,对每一块都分别进行统计,当计算完的时候,当总数为0的时候,又因为m不等于0,所以最多是2个点相连,所以总数就是m*(n-2),即m里选择条边,n-2选择一个点,然后输出,这时候我们必须添加两条边,如果总数大于0,则最多只要添加一条边,输出答案就好,最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; vector<int> V[maxn]; int n,m; void init() { for(int i=1; i<=n; i++)V[i].clear(); int u,v; for(int i=1; i<=m; i++) { scanf("%d%d",&u,&v); V[u].push_back(v); V[v].push_back(u); } } bool vis[maxn]; bool color[maxn]; LL cc[2]; bool flag; LL MIN; void DFS(int now) { vis[now]=1; cc[color[now]]++; for(int i=0; i<V[now].size(); i++) { int v=V[now][i]; if(!vis[v]) { color[v]=!color[now]; DFS(v); } else if(color[v]==color[now]) { flag=1; } } } void play() { if(m==0){ LL ans=1LL*n*(n-1)*(n-2)/6; printf("3 %I64d\n",ans); return; } memset(vis,0,sizeof(vis)); flag=0; LL ans=0; memset(color,0,sizeof(color)); for(int i=1; i<=n; i++) { if(vis[i])continue; cc[0]=cc[1]=0; DFS(i); if(flag){ printf("0 1\n"); return; } ans+=cc[0]*(cc[0]-1)/2; ans+=cc[1]*(cc[1]-1)/2; } if(ans==0){ printf("2 %I64d\n",1LL*m*(n-2)); return; } printf("1 %I64d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R311 DIV2 C. Arthur and Table
飞机票:http://codeforces.com/contest/557/problem/C
题意:给你个桌子,有n个桌脚(n<=1e5),每个桌脚有个高度为ai,拆掉每个桌脚需要力量bi,现在问你拆除桌脚使桌子稳定,且用力最小,桌子稳定的条件是最大的那个脚的总数必须大于当前脚数量的一半,假设有当前有5个桌脚,要使稳定,需要3个最大的脚,假设4个,则要3个最大脚,只有1个他就是稳定的。
分析:我们可以枚举脚的长度,怎么枚举??按脚长度排个序,然后假设长度为k的脚的数量从l到r,即有r-l+1个为k的数量。然后我们可以来个辅助数组ci,就是大于k脚长度需要花费的总力气,这个力气可以从后往前推,因为我们是从小到大对脚长度进行排序,那么初始必须花费的力气是c[r+1],然后我们有r-l+1的数量,设为sum,则我们可以保留sum-1(因为大于一半)个脚,因为前面有l-1个脚,我们可以拆除l-1-(sum-1)个脚。设为des,如果des<=0,则不用拆了,因为我们已经足够,如果大于0,则要从前面l-1选择des个脚拆除,我们贪心的话,肯定会拆需要力量最小的脚,因为力量<=200,所以我们可以用个数组need[200]表示前面各个力量有多少个然后从小到达选择des个,设为力量需要为p,这总共需要力量c[r+1]+p,然后去最小值因为我们要处理need[200],所以我们要一个辅助数组记录l到r需要的各个力量,然后处理完l~r则把辅助数组的数据加到need上去,让下一次可以继续迭代,复杂度O(n*200),最后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <queue> #include <vector> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; int n; struct he { int len,val; } Sky[maxn]; bool cmp(he a, he b) { return a.len<b.len; } int bi[maxn]; int ai[234]; int ci[234]; void init() { for(int i=1; i<=n; i++) { scanf("%d",&Sky[i].len); } for(int i=1; i<=n; i++) { scanf("%d",&Sky[i].val); } sort(Sky+1,Sky+1+n,cmp); bi[n+1]=0; for(int i=n; i>=1; i--) { bi[i]=Sky[i].val; bi[i]+=bi[i+1]; } Sky[n+1].len=INF; Sky[0].len=0; } void play() { memset(ai,0,sizeof(ai)); int l=-1; int MIN=INF; memset(ci,0,sizeof(ci)); for(int i=1; i<=n+1; i++) { if(Sky[i].len!=Sky[i-1].len) { if(l!=-1) { int ok=(i-1-l)+1; int xy=ok-1; xy=l-1-xy; int sum=bi[i]; if(xy>0) { for(int j=1; j<=200; j++) { if(xy<=ai[j]) { sum+=xy*j; break; } else { xy-=ai[j]; sum+=ai[j]*j; } } } if(sum<MIN)MIN=sum; } for(int j=1; j<=200; j++) { ai[j]+=ci[j]; } l=i; memset(ci,0,sizeof(ci)); int v=Sky[i].val; ci[v]++; } else { int v=Sky[i].val; ci[v]++; } } printf("%d\n",MIN); } int main() { while(scanf("%d",&n)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R310 DIV2 D. Case of Fugitive
飞机票:http://codeforces.com/contest/556/problem/D
题意:给你n个区间li,ri,且ri<li+1,(1<=i<n),li<=ri,然后给你m座桥,每个有个长度ai,然后要求把桥搭在区间上,且满足桥的长度
大于等于li+1-ri,且小于等于ri+1-li,求能否用桥把所有的相邻区间连接起来,并且输出每个相邻区间对应的桥下标。
分析:起先一看以为是二分图,但是看边太多直接放弃。这里我们 可以知道一个东西。就是相邻区间需要的桥的最大值和最小值,假设两个相邻区间li,ri li+1,ri+1,我们定义他们的坐标为i,则可知需要的最小长度为li+1-ri,最大长度为ri+1-li,假设为最小为le最大为ri,接着我们再把m条桥排序,从小到大。接着运用贪心看看怎么贪?假设当前要判断桥i属于哪个区间le ri,我们必须把所有可以用桥i的区间找出来,然后找右端点最小的,为什么?因为大伙都可以用它,当然我们要使用剩余使用量最小的来接受它,不然给别人,它都亏。搞到这里就知道怎么贪了吧。就是把le ri按左区间排序,如果一样不用管。然后来个优先队列,假如当前的le<=桥i的长度,就把它加到优先队列,为什么??因为我们不知道它满不满足,但是只要它的右端点最小,然后比桥i的长度还短,那么直接就No了,因为别人都比它好。优先队列干嘛用的?就是取右端点最小的,因为我们前面判断过的比较小的le可能要在这个时候用到。总体复杂的就是O(n*logn)了。然后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=2*123456; LL l[maxn],r[maxn]; struct he { int id; LL l,r; bool operator<(const he& a)const { return r>a.r; } } Sky[maxn]; LL ai[maxn]; struct he1 { LL val; int id; } Sky1[maxn]; int can[maxn]; int n,m; bool cmp(he1 a,he1 b) { return a.val<b.val; } void init() { for(int i=1; i<=n; i++) { scanf("%I64d%I64d",&l[i],&r[i]); } for(int i=1; i<=m; i++) { scanf("%I64d",&Sky1[i].val); Sky1[i].id=i; } sort(Sky1+1,Sky1+1+m,cmp); } bool cmp2(he a,he b) { if(a.l!=b.l)return a.l<b.l; return a.r<b.r; } priority_queue<he> pq; void play() { int st=0; for(int i=2; i<=n; i++) { LL a1=l[i]-r[i-1]; LL a2=r[i]-l[i-1]; st++; Sky[st].l=a1; Sky[st].r=a2; Sky[st].id=st; } sort(Sky+1,Sky+1+st,cmp2); while(!pq.empty())pq.pop(); int now=1; for(int i=1; i<=m; i++) { int id=Sky1[i].id; LL val=Sky1[i].val; while(now<=st) { if(Sky[now].l<=val) { pq.push(Sky[now]); now++; } else { break; } } if(pq.empty())continue; he aa=pq.top(); pq.pop(); if(aa.r<Sky1[i].val) { printf("No\n"); return; } int idx=aa.id; can[idx]=id; } if(!pq.empty()||now!=st+1) { printf("No\n"); return ; } printf("Yes\n"); for(int i=1; i<=st; i++) { printf("%d ",can[i]); } printf("\n"); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R310 DIV2 C. Case of Matryoshkas
飞机票:http://codeforces.com/contest/556/problem/C
题意:就是给你k个已经按大小递增的序列,每个有mi个1<=mi<=n,m1+m2+,,mk=n,其中1到n只出现一次。求组合成从1到n的序列最小的时间值,具体的每秒能做的事是,把序列拆掉或把序列合并,拆掉的话必须有一个是独立的不能两个子序列一起拆开,比如序列1-2-3-4,可以把1或4拆下来,但是不能拆成1-2 3-4这样是非法的。合并的话也是有规律的,就是以1为首的字串不能何在一个序列长度大于1的序列里面,这样也是非法的,比如序列1-2-3 和序列5-6合并成1到6要3秒,因为5-6包含2长度,所以要拆开,成为5和6,然后5合并,6合并。题意比较坑爹啊~
分析:知道题意后就可以瞎搞了,很简单,就是把包含1的序列后面能递增到几个拿出来先,如果后面没了,初始时间就是0,如果后面还有就是1,因为要拆开,然后后面的序列都要拆,因为要把包含1的合并进去,所以假设序列长度为len,则拆要len-1,合并要len,所以共要len*2-1,所有加起来就是答案,然后啪啪啪就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; int idx[maxn]; vector<int> V[maxn]; int first[maxn]; int n,k; LL MIN; void init() { int m; MIN=0; for(int i=1; i<=k; i++) { V[i].clear(); first[i]=0; scanf("%d",&m); bool flag=1; for(int j=1; j<=m; j++) { int u; scanf("%d",&u); if(u==1) { flag=0; int p=1; for(int z=j+1; z<=m; z++) { j=z; scanf("%d",&u); if(u==++p)continue; MIN+=(m-z+1)*2; break; } } } if(flag){ MIN+=(m*2)-1; } } } void play() { printf("%I64d\n",MIN); } int main() { while(scanf("%d%d",&n,&k)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R308 DIV2 E. Vanya and Brackets
飞机票:http://codeforces.com/contest/552/problem/E
题意:给你个没有括号的正确表达式,其中只包含*和+,和1~9的数字,添加一个括号,使得最后的数字最大。
分析:假设一共有n个数字,其实就是在选择两个标i和j(i<=j)使得1~i-1和i~j和j+1~n的计算最大。具体可以维护前缀和后缀,具体需要维护哪些值,前缀的话,假设从1~i,我们维护这个值的大小,但是还不够,因为我们不知道紧接的是*还是+,如何是+的话还是很好处理的,只要把两个相加就好了,如果是乘,我们还得维护1~i最后一个+后面的数字的大小,因为我们要把它与后面的数字相乘然后后缀也是一样的,维护相同的值,不一样的是方向相反,因为我们如果从前面开始,后缀就没办法维护了。具体求数组的方法是使用栈来求(这里就不细讲,是每个人应该会的)假设我们已经求的前缀的和li和前缀要维护最右边的数字lvali 和后缀的和ri和维护最左边的值rvali,接下来就是枚举需要加括号的地方。假设为在i前面加和在j后面加。则有
求的前缀到i-1的值为 ls,后缀到j+1的值为rs,且lval为v1 ,rval为v2,中间的i到j的结果为s。
(1)如果i前面的符号是*和j后面的符号是*,则大小sum就为ls+rs-v1-v2+v1*s*v2
因为v1和v2要和s乘为新的数,所以要减去。
(2)如果只有i前面的符号是*,则大小sum为ls-v1+v1*s+rs;
因为后面的话是直接加上去的。
(3)如果只有j+1后面的符号为*,则跟(2)差不多性质
(4)如果i从1开始,则只要考虑rs的情况
(5)如果j到了n,则只要考虑ls的情况
复杂度为O(n*n)然后啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=5678; char ch[maxn]; int ai[maxn]; int bi[maxn]; int len; LL lr[2][maxn]; LL limit[2][maxn]; int st; void get() { for(int i=0; i<2; i++) { int s=1; int c=1; int cz=-1; if(i==1) { s=st; c=-1; cz=0; } lr[i][s]=ai[s]; vector<LL> V; LL sum=ai[s]; V.push_back(ai[s]); limit[i][s]=ai[s]; for(int j=1; j<st; j++) { s=s+c; if(bi[s+cz]==1) { LL q=V[V.size()-1]; LL p=q*ai[s]; V[V.size()-1]=p; sum=sum-q+p; } else { V.push_back(ai[s]); sum=sum+ai[s]; } lr[i][s]=sum; limit[i][s]=V[V.size()-1]; } } } void init() { len=strlen(ch+1); st=0; for(int i=1; i<=len; i++) { if(ch[i]=='*') bi[st]=1; else if(ch[i]=='+') bi[st]=0; else { st++; ai[st]=ch[i]-'0'; } } get(); } void play() { LL MAX=0; for(int i=1; i<=st; i++) { vector<LL>V; LL sum=0; for(int j=i; j<=st; j++) { if(j==i) { V.push_back(ai[i]); sum=ai[i]; } else { if(bi[j-1]==1) { LL q=V[V.size()-1]; LL p=q*ai[j]; V[V.size()-1]=p; sum=sum-q+p; } else { V.push_back(ai[j]); sum=sum+ai[j]; } } int ln=i-1; int rn=j+1; LL ll=lr[0][ln]; LL rr=lr[1][rn]; if(ln>=1&&rn<=st) if(bi[ln]==1&&bi[j]==1) MAX=max(MAX,ll+rr-limit[0][ln]-limit[1][rn]+limit[0][ln]*limit[1][rn]*sum); else if(bi[ln]==1) MAX=max(MAX,ll+rr-limit[0][ln]+limit[0][ln]*sum); else if(bi[j]==1) MAX=max(MAX,ll+rr-limit[1][rn]+limit[1][rn]*sum); else MAX=max(MAX,ll+rr+sum); else if(ln>=1) if(bi[ln]==1) MAX=max(MAX,ll-limit[0][ln]+limit[0][ln]*sum); else MAX=max(MAX,ll+sum); else if(rn<=st) if(bi[j]==1) MAX=max(MAX,rr-limit[1][rn]+limit[1][rn]*sum); else MAX=max(MAX,rr+sum); else MAX=max(MAX,sum); } } printf("%I64d\n",MAX); } int main() { while(scanf("%s",ch+1)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R308 DIV2 D. Vanya and Triangles
飞机票:http://codeforces.com/contest/552/problem/D
题意:给你n个点,两两相连,一共有多少个三角形是已给出n个点中三个点组成的
分析:这题的话O(n3)能过,我不知道出题人是什么意思,,,我的话是n*40000.最多也就O(10*n*n)的复杂度,跑了173ms。直接进入正题:此题可以用计数容斥来做,具体就是对与每个点i 对(i+1~n)进行枚举。(1<=i<=n)具体的做法是:假设当前的点是i,枚举(i+1~n)和i的斜率,并把斜率存入到数组中,因为最大的yj-yi=200 xj-xi=200,所以存入到200*200的数组中就可以了但是这里我们必须把(yj-yi)和(xj-xi)的最大公约数求出来,假设除了最大公约数后分别为p和q,因为这样才可以知道斜率为p/q的点关于i有多少个。这里的话,我们可以预处理下1~200每对的最大公约数,这样就是O(1)的时间求最大公约数了。而且我是用了两个数组,一个存斜率是正数,一个存斜率是负数,而且还得存p=0或q=0的情况。最后就是对三角形的个数进行统计,假设斜率为k的点有有l个,除了l个点和i点后有r个点,则三角形个数就是l*r,因为l上选一个点,i一个点,r上一个点,计算完后,需要把重复的除掉,因为对于两个点a和b,我们计算了a到b和b到a的,所以最后除以2就可以了,然后加到总数上去最后啪啪啪,就可以AC了。
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; int x1[201][201]; int y11[201][201]; int G[201][201]; const int maxn=2*1234; struct he { int x,y; } Sky[maxn]; int n; void init() { for(int i=1; i<=n; i++)scanf("%d%d",&Sky[i].x,&Sky[i].y); } void play() { LL ans=0; for(int i=1; i<=n; i++) { memset(x1,0,sizeof(x1)); memset(y11,0,sizeof(y11)); int l=0; int r=0; for(int j=i+1; j<=n; j++) { int q=Sky[i].y-Sky[j].y; int p=Sky[i].x-Sky[j].x; if(p==0) { l++; } else if(q==0) { r++; } else { int nn=G[abs(q)][abs(p)]; q/=nn; p/=nn; if(q>0&&p>0) { x1[q][p]++; } else if(q<0&&p<0) { x1[-q][-p]++; } else { y11[abs(q)][abs(p)]++; } } } LL sum=0; int sk=n-i; if(l!=0) { sum+=(sk-l)*l; } if(r!=0) { sum+=(sk-r)*r; } for(int j=1; j<=200; j++) { for(int k=1; k<=200; k++) { int p=x1[j][k]; int q=y11[j][k]; if(p!=0) { sum+=(sk-p)*p; } if(q!=0) { sum+=(sk-q)*q; } } } sum/=2; ans+=sum; } printf("%I64d\n",ans); } int GCD(int a,int b) { if(a<b)swap(a,b); if(a%b!=0) { return GCD(b,a%b); } return b; } int main() { for(int i=1; i<=200; i++) { for(int j=1; j<=200; j++) { G[i][j]=GCD(i,j); } } while(scanf("%d",&n)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R308 DIV2 C. Vanya and Scales
飞机票:http://codeforces.com/contest/552/problem/C
题意:给你w和m,然后用w0.w1.w2......w100,放在天平两侧,且m肯定要放,w可以用完,但是每个只有一个,使得天平平衡。
分析:数学题,我们假设有个数组ai,且长度为0~100,且ai对应w0.w1.w2......w100其中,ai可以为1,0,-1,我们现在的目标就很明确了a0*w0+a1*w1+a2*w2......a100*w100=m如果ai为1的话,就是说指数为i的要放在天平与m相反的盘子里,如果是0,就是这个数不用用,如果是1,就是放在与m相同的地方。然后我们可以提取这些数的最大公约数,则有w0(a0+a1*w1/w0+a2*w2/w0...... )=m
从这个公式我们可以推出,m必须要可以整除w0,因为不能整除就是NO了,然后就剩下(a0+a1*w1/w0+a2*w2/w0...... )=m/w0 ,把a0移过来(a1*w1/w0+a2*w2/w0...... )=m/w0 +a0 则左边可以用前面的方法继续迭代,右边的话a0有三种可能,且如果这三种可能没有一种可以使得右边整除左边的最大公约数,则也是NO,因为w>2,所以这三种可能做多就一种满足。然后当右边=1的时候就结束,因为左边和右边一样了,YES。然后啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <map> #include <stack> #include <vector> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; int w,m; void init(){ } void play(){ int t=0; while(1){ if(m==1){ printf("YES\n"); return; } if(m%w!=0){ if((m+1)%w==0){ m=m+1; m/=w; } else if((m-1)%w==0){ m=m-1; m/=w; } } else{ m/=w; } t++; if(t==101){ printf("NO\n"); return; } } } int main() { while(scanf("%d%d",&w,&m)!=EOF){ init(); play(); } // cout << "Hello world!" << endl; return 0; }
R307 DIV2 C. GukiZ hates Boxes
飞机票:http://codeforces.com/contest/551/problem/C
题意:有一个数组ai[],有n个元素,ai[i]代表第i个元素的值,有m个学生,学生在最左边,且到第i个元素要i秒,且使第i个元素值减1需要花费1秒,求m个人使所有值为0最少要多少时间,学生可以同时行动。
分析:题目一看,必然二分啊,但是怎么维护这个值呢?这里有2种方法,假设当前判断的值是mid
(1)从后往前推:假设最右边不为0的下标为k,则学生到k需要花费k秒,如果mid<=k直接就不行,因为到了k这个点就没时间了。所以mid肯定要大于k,然后ai[k]就要减掉mid-k的值,这样就是最优的,因为学生的时间没有一秒浪费。
(2)从前往后推:这个情况有点难理解,只能YY了,假设当前的人在i,且后面有值的地方在j(i<j),如果当前这个人再拿一个i的值,他到不了j或则到了j已经没有时间了,这样就有时间浪费了,但是我们会发现,下个人他就多了1秒的时间,就是他如果能到j,则他可以多拿一个,并不会影响到最优值。有点难理解哇~
我的代码是用第二种方法的,复杂度为O(log2INF*n)INF=2*1e18,要及时跳出循环,不然就TLE了,啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <map> #include <stack> #include <queue> #define LL long long #define INF 1e20 #define EPS 1e-9 using namespace std; const int maxn=123456; LL ai[maxn]; LL bi[maxn]; int n,m; void init() { for(int i=1; i<=n; i++) { scanf("%I64d",&ai[i]); bi[i]=ai[i]; } } void play() { LL l=0; LL r=INF; LL ans=r; LL p=INF; for(int i=n; i>=1; i--) { if(ai[i]>0) { p=i; break; } } if(p==INF) { printf("0\n"); return; } n=p; while(l<=r) { LL mid=(l+r)/2; int st=1; for(int i=1; i<=m; i++) { LL t=mid; int ti=st; for(int j=st; j<=n; j++) { LL now=ai[j]+ti; if(now>t) { st=j; t-=ti; if(t>0) { ai[j]-=t; } break; } else { t-=now; ai[j]=0; ti=1; st=j; } } if(st==n&&ai[n]<=0)break; } bool flag=0; if(st==n&&ai[n]<=0)flag=1; LL p=ai[n]; for(int i=1; i<=n; i++)ai[i]=bi[i]; if(!flag) { l=mid+1; } else { r=mid-1; ans=mid; } } printf("%I64d\n",ans); } int main() { while(scanf("%d%d",&n,&m)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R307 DIV2 B. ZgukistringZ
飞机票:http://codeforces.com/contest/551/problem/B
题意:给你三个字符串,a,b,c,只包含小写字母,求字符串a重新排列后k包含最多的不重叠字串,为b或则为c.
分析:先想想先重组b或则c,会发现这个贪心不正确,因为可能重组一次b后,后面先重组c会比较优然后我就卡题了,一点感觉都没有,而且还分三种情况先b再c,或c再b,或则两个一起来,后面才发现这种贪心明显是错的,因为不是最优的。一直wa test12后来看了下字符串长度为1e5,可以暴力直接过。o(︶︿︶)o ,弱死了,想了半天,至于暴力,就是先预处理出都重组b的情况,假设为sum1,然后枚举sum1,推出重组c的情况sum2,求sum1+sum2的最大值,然后输出,复杂度0(sum1*26)sum1<=100000,所以还是非常快的,啪啪啪,就可以AC了
/* Author:Chieh Because Of Coding */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <map> #include <stack> #include <queue> #define LL long long #define INF 1e9 #define EPS 1e-9 using namespace std; const int maxn=123456; char ch1[maxn]; char ch2[maxn]; char ch3[maxn]; int ai[27]; int bi[27]; int ci[27]; int len1,len2,len3; void init() { len1=strlen(ch1+1); len2=strlen(ch2+1); len3=strlen(ch3+1); memset(ai,0,sizeof(ai)); memset(bi,0,sizeof(bi)); memset(ci,0,sizeof(ci)); for(int i=1; i<=len1; i++) { int t=ch1[i]-'a'+1; ai[t]++; } for(int i=1; i<=len2; i++) { int t=ch2[i]-'a'+1; bi[t]++; } for(int i=1; i<=len3; i++) { int t=ch3[i]-'a'+1; ci[t]++; } } void play() { int sum1=1000000000; for(int i=1; i<=26; i++) { if(bi[i]>0) { sum1=min(sum1,ai[i]/bi[i]); } } int l=-1,r=-1; for(int i=0; i<=sum1; i++) { for(int j=1; j<=26; j++) { ai[j]-=(bi[j]*i); } int sum2=1000000000; for(int j=1; j<=26; j++) { if(ci[j]>0) { sum2=min(sum2,ai[j]/ci[j]); } } for(int j=1; j<=26; j++) { ai[j]+=(bi[j]*i); } if(l==-1&&r==-1) { l=i; r=sum2; } else { if(l+r<i+sum2) { l=i; r=sum2; } } } for(int i=1; i<=l; i++)printf("%s",ch2+1); for(int i=1; i<=r; i++)printf("%s",ch3+1); for(int i=1; i<=l; i++) { for(int j=1; j<=26; j++) { ai[j]-=bi[j]; } } for(int i=1; i<=r; i++) { for(int j=1; j<=26; j++) { ai[j]-=ci[j]; } } for(int i=1; i<=26; i++) { for(int j=1; j<=ai[i];j++)printf("%c",'a'+i-1); } printf("\n"); } int main() { while(scanf("%s%s%s",ch1+1,ch2+1,ch3+1)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }
R290 DIV2 C. Fox And Names
飞机票:http://codeforces.com/problemset/problem/510/C
题意:就是给你n个字符串,按字典序从上到下递增,判断字典序是否正确,正确输出任意一个可行的方案,不行则impossible
分析:刚开始看对题,但是自己想法错误,大晚上想法不行啊后来还是想回来了,我的想法是分治法。
假设n个字符串,第i个到第j个(i<j)的第一个字符都是一样的,则它们还得比较第二个字符。。。。第k个字符,直到不一样为止。
当不一样时我们就可以加边了,假设不一样的字符是第k个,则字符串i的第k个字符<字符串j的第k个字符,加单向边,一直到最后,然后用floyd判断有无环,有环则impossible,没环则输出可行方案可以方案怎么输出,我是枚举的,因为数量不大,假设当前的字符是i,则判断有没有j可以到i的,如果j输出过了,则跳过,如果没有,则可以证明i是当前最小的,把i输出来,然后标记i就行了。。。这里还有个地方,就是字符串长度的问题,假设到比较第k个字符串了,来个标记,如果从i到p是空的,p+1是不空的,则改变标记,这样后面如果有空的,就是impossible,直接返回~
PS:大晚上明明返回标记了,后面忘了判断输出了,fst了改一下就AC了
/* Author:Chieh Because of You */ #include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <queue> #include <map> #include <vector> #include <stack> #define LL long long using namespace std; map<char,int> M; map<int,char> M1; const int maxn=30; int ai[maxn][maxn]; char ch[123][123]; int n; int len; void init() { len=0; for(int i=1; i<=n; i++) { scanf("%s",ch[i]+1); int q=strlen(ch[i]+1); len=max(len,q); } } bool flag; int st; void DFS(int now,int l,int r) { if(!flag)return ; char before='A'; int ks=0; int os=-1; for(int i=l; i<=r; i++) { int q=strlen(ch[i]+1); char p=ch[i][now]; if(now>q&&os==-1)continue; os=1; if(now>q) { flag=0; return; } if(before=='A') { ks=l; before=ch[i][now]; } else { if(before==p) { continue; } else { if(M[before]==0) { M[before]=st; M1[st]=before; st++; } if(M[p]==0) { M[p]=st; M1[st]=p; st++; } int u=M[before]; int v=M[p]; before=p; ai[u][v]=1; if(i-1==ks) { ks=i; continue; } else { DFS(now+1,ks,i-1); } ks=i; } } } if(r==ks)return; else if(ks==0)return; DFS(now+1,ks,r); } bool vis[maxn]; void play() { memset(ai,0,sizeof(ai)); memset(vis,0,sizeof(vis)); flag=1; M.clear(); M1.clear(); st=1; DFS(1,1,n); if(!flag) { printf("Impossible\n"); return; } for(int k=1; k<=29; k++) { for(int i=1; i<=29; i++) { for(int j=1; j<=29; j++) { if(ai[i][k]&&ai[k][j]) { ai[i][j]=1; } } } } for(int i=1; i<=29; i++) { for(int j=1; j<=29; j++) { if(ai[i][j]&&ai[j][i]) { printf("Impossible\n"); return; } } } for(char i='a'; i<='z'; i++) { if(M[i]==0)printf("%c",i); } while(1) { bool ok=0; for(int i=1; i<=29; i++) { if(vis[i])continue; if(M1[i]!=0) { bool flag=1; for(int j=1; j<=29; j++) { if(vis[j])continue; if(M1[j]==0)continue; if(ai[j][i]) { flag=0; break; } } if(flag) { printf("%c",M1[i]); vis[i]=1; ok=1; } } } if(!ok)break; } printf("\n"); } int main() { while(scanf("%d",&n)!=EOF) { init(); play(); } // cout << "Hello world!" << endl; return 0; }