陕西省集训day2(二分,贪心,枚举续)
2016-08-20 16:41
369 查看
其实是上一篇的续啦,还是那场比赛
http://acm.hust.edu.cn/vjudge/contest/125415#overview
②判断数字个数,若n为奇数个,则只能有一个数字个数是奇数个,偶数则不能有数字个数是奇数个的。否则肯定构不成回文,答案为0
③从两头往中间判断,相同的把数字个数-2,这些数字不是必须要重排的(可排可不排),当找到两个数字不同的时候,就需要两头各找出一个最小区间,使得他们重排后为回文。
要找最小区间,容易想到二分找,但怎么判断该区间满足条件就比较麻烦。首先得确定二分的下界,若中间有部分数字相同的话,则区间有可能只存在一边或两边各占,下界就是中间开始不同的地方,上界就是③中对应另一个不同的数字位置,若中间数字完全没有相同的话,则一定是两边各占,这样下界就是(n+1)/2,上界也是③中对应另一个不同的数字位置。
至于判断区间是否满足,假设当前区间长度为len1,另一半区间为len2,则只要比较两个区间大小,然后找到小的那个,然后判断该区间是否有数字大于该数字总的一半即为不满足,否则满足。
获得两个最小区间后,用容斥定律来解,假设两个区间为[l1,r1],[l2,r2]。左区间的总方案=l1*(n-r1+1),右区间总方案=(n-r2+1)l1。减去的共同的方案数=l1(n-r2+1)
http://acm.hust.edu.cn/vjudge/contest/125415#overview
I题:
http://codeforces.com/problemset/problem/442/A题意:
小明有25种牌,每张有花色和数值,现在他知道手里有哪些牌,但是不知道哪个是哪个。 旁边的人可以提示他花色,把某个花色的牌告诉他,或者数值,同理。求最少提示次数。思路:
注释很清晰,用二进制代表花色或者数字是否已知#include<cstdio> #include <iostream> using namespace std; int n; char a[110][6]; int b[110][2]; int ans; int change(int x) //换成二进制数后,各个位置加起来表示告诉了多少个提示 { int res=0; while(x) { res+=x%2; x/=2; } return res; } int main() { int i,j,k,l; scanf("%d",&n); for(i=0; i<n; i++) { scanf("%s",a[i]); if(a[i][0]=='R')b[i][0]=0; else if(a[i][0]=='G')b[i][0]=1; else if(a[i][0]=='B')b[i][0]=2; else if(a[i][0]=='Y')b[i][0]=3; else if(a[i][0]=='W')b[i][0]=4; b[i][1]=a[i][1]-'1'; } ans=10; //二进制数字代表字母/数字那5位是否已知,知道的话该位是1,最后多少个1就知道多少个已知提示 for(i=0; i<32; i++) { for(j=0; j<32; j++) { for(k=0; k<n; k++)//枚举每一张牌 { // cout << b[k][0]<<endl; for(l=k+1; l<n; l++) { //cout <<b[k][0] <<endl; if(b[k][0]==b[l][0]&&b[k][1]==b[l][1])continue; //把后面的与当前位完全相同,字母和数字都相同,不区分,相当于已知 else if(b[k][0]!=b[l][0]&&(i>>(b[k][0]))%2)continue; //不相同但是k的字母已知 else if(b[k][0]!=b[l][0]&&(i>>(b[l][0]))%2)continue;//不相同但是l的字母已知 else if(b[k][1]!=b[l][1]&&(j>>(b[k][1]))%2)continue;//k的数字已知 else if(b[k][1]!=b[l][1]&&(j>>(b[l][1]))%2)continue;//l的数字位已知 else break;//两个都不知道就惨了 } if(l!=n)break;//两个都不知道的话说明当前的i,j不符合条件 } if(k==n) ans=min(ans,change(i)+change(j) );//所有的牌都符合了条件,全部已知。 } } printf("%d",ans); }
L题:
题意:
给出n个数,若有一个区间,只重排那个区间的数字(可不排),使得数组是构成一个回文,则它就是答案之一思路:
①若本身就是回文,则答案就是1+2+……+n-1+n,即(1+n)*n/2②判断数字个数,若n为奇数个,则只能有一个数字个数是奇数个,偶数则不能有数字个数是奇数个的。否则肯定构不成回文,答案为0
③从两头往中间判断,相同的把数字个数-2,这些数字不是必须要重排的(可排可不排),当找到两个数字不同的时候,就需要两头各找出一个最小区间,使得他们重排后为回文。
要找最小区间,容易想到二分找,但怎么判断该区间满足条件就比较麻烦。首先得确定二分的下界,若中间有部分数字相同的话,则区间有可能只存在一边或两边各占,下界就是中间开始不同的地方,上界就是③中对应另一个不同的数字位置,若中间数字完全没有相同的话,则一定是两边各占,这样下界就是(n+1)/2,上界也是③中对应另一个不同的数字位置。
至于判断区间是否满足,假设当前区间长度为len1,另一半区间为len2,则只要比较两个区间大小,然后找到小的那个,然后判断该区间是否有数字大于该数字总的一半即为不满足,否则满足。
获得两个最小区间后,用容斥定律来解,假设两个区间为[l1,r1],[l2,r2]。左区间的总方案=l1*(n-r1+1),右区间总方案=(n-r2+1)l1。减去的共同的方案数=l1(n-r2+1)
#include<cstdio> #include<cstring> #include<iostream> #include<map> using namespace std; #define LL long long const int maxn=100005; map<int,int> mp,mp1,mp2; map<int,int> ::iterator it; struct sw{ int l,r; sw(){} sw(int ll,int rr){ l=ll,r=rr; } }ans[2]; int n; int num[maxn]; int flag,cou=0,cou2=0,now,key; unsigned LL res=0; bool check(int l,int r){ mp1.clear(); int len1=r-l+1,len2=n-2*now-len1; if(len2<len1){ if(l==now+1){ r=n-l+1; l=r-len2+1; } else { l=n-r+1; r=l+len2-1; } } for(;l<=r;l++) mp1[num[l]]++; for(it=mp1.begin();it!=mp1.end();it++){ if(it->second>mp[it->first]/2) return 0; } return 1; } int main(void){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&num[i]); mp[num[i]]++; } flag=n%2; for(it=mp.begin();it!=mp.end();it++){ if(it->second%2) cou++,key=it->first; if(cou>flag) break; } if(cou>flag) { printf("0\n"); return 0; } for(int s=1,e=n;s<e;s++,e--){ if(num[s]!=num[e]){ now=s-1; int l=1,r=e-s; for(int i=(n+1)/2,j=(n+2)/2;1;i--,j++) if(num[i]!=num[j]||(flag&&i==(n+1)/2&&num[i]!=key)){ key=i-s; l=key; break; } while(l<=r){ int mid=(l+r)>>1; if(check(s,s+mid)) r=mid-1; else l=mid+1; } ans[cou2++]=sw(s,s+l); l=key,r=e-s; while(l<=r){ int mid=(l+r)>>1; if(check(e-mid,e)) r=mid-1; else l=mid+1; } ans[cou2++]=sw(e-l,e); break; } else mp[num[s]]-=2; } if(cou2==0){ res=1LL*(n+1)*n/2; cout<<res<<endl; return 0; } { int l1,l2,r1,r2; l1=ans[0].l,l2=ans[1].l; r1=ans[0].r,r2=ans[1].r; res+=l1*(n-r1+1)+(n-r2+1)*l2; res-=l1*(n-r2+1); cout<<res<<endl; } return 0; }
相关文章推荐
- 陕西省集训day1(枚举,贪心,二分)
- codeforces 613B B. Skills(枚举+二分+贪心)
- 二分枚举+贪心(nyist疯牛)
- NYOJ 586疯牛 和 NYOJ 619青蛙过桥 (二分枚举+贪心)
- 二分枚举 +贪心 NOJ 680 摘枇杷
- 集训第四周(高效算法设计)F题 (二分+贪心)
- POJ 2456 Aggressive cows 和 NYOJ 586 疯牛【二分枚举+贪心】
- 跳石头_NOIP2015 Day2 T1_二分+贪心
- nyoj 586 疯牛和nyoj 619 青蛙过桥 二分枚举+贪心
- 湖南NOIP集训模拟题DAY1 BY ExfJOE [贪心][DP][二分]
- POJ 1505:二分枚举+贪心
- UVA 1616 Caravan Robbers 【二分+贪心+枚举分母】
- NYOJ 586.疯牛(二分枚举+贪心)
- POJ1505:Copying Books(贪心+二分枚举)
- NYOJ-586 疯牛(二分枚举+贪心)
- 【Codeforces613B】Skills【二分】【枚举】【贪心】
- 湖南集训Day2
- Hrbust 1201 Zombie’s Treasure Chest【贪心+暴力枚举】
- [霍尔定理]「2017 山东一轮集训 Day2」LOJ 6062——PAIR
- CF Good Bye 2015 E. New Year and Three Musketeers(贪心+枚举)