And Then There Was One, Japan 2007,LA 3882 题解
Vjudge传送门
题意:
n
个数排成一圈,第一次删除
数据范围:多组数据,
题解:
显然我们已经不能模拟了,因为数据规模实在是太大啦,已经不再是猴子选大王靠模拟就可以搞出来的了。然而怎么做呢,我们可以用递推搞出来嘛?仿佛是可以的,因为如果是
n
个猴子选大王,第一个猴子被踢出去之后,剩下不就是
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int n,k,m,f[10000+1000];
int main(){
while(scanf("%d%d%d",&n,&k,&m)!=EOF&&n&&k&&m){
f[1]=0;
for(register int i=2;i<=n;i++)f[i]=(f[i-1]+k)%i;
int ans=(m-k+1+f[n])%n;
if(ans<=0) ans+=n;
printf("%d\n",ans);
}
return 0;
}
UVaLive 3905 Meteor ,Seoul 2007 题解
Vjudge传送门
题意:
给你n个点,然后这n个点会按照给出的x轴速度,y轴速度运动,我们需要求得是,现在给你一个固定的相框,输出n个点出现在这个固定的相框里最多的时候一共有n个中的多少个点
题解:
如果直接正面去求这个,会发现很难算,所以我们需要转化思路,既然是求n个点中某一时刻尽量出现更多的点在这个框里,那么我们的想法是逆向思维,先把n个点出现的时刻先计算出来,然后再记录到一个结构体里面,即一个区间l,r,表示这个点在l,r这段时间内出现次数最多,于是我们可以得出一个算法,求解在某一时刻,重叠的区间数最多,输出区间最多是多少个,显然我们可以把左端点和右端点当做两个事件,也就是说,对所有事件排了序之后我们从左至右扫描(因为时间是向量,只能线性地流逝不能纵向叠加[当然对于蛤来说除外]),如果扫到一个左端点,那么说明新进来了一个区间,如果扫到一个右端点,那么说明出去了一个区间,注意一点有点坑,也就是这道题在框上的点是不算的,因此我们需要把右端点的事件的优先级设置为比左端点大,也就是说倘若是同一个时间,一个是左端点,一个是右端点,那么右端点这个时间的优先级高于左端点,这样一来会先扫描到右端点,再扫描到左端点,也就是先出再进,才能保证答案的正确性,至此我们就已经得到了完整的算法,在计算一个点在框内的时间的时候有一个小技巧,也就是先计算x坐标位于相框区间的时间,再计算y坐标位于相框区间的时间,这样两个时间的交集显然就是x坐标位于相框区间,y坐标位于相框区间的时刻,这道题貌似没有eps的问题,但是其实可以转化成整数,貌似是同时乘以一个什么公倍数。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
void update(int x,int a,int w,double& L,double& R){
if(a==0){
if(x<=0||x>=w) R=L-1;
}else if(a>0){
L=max(L,-(double)x/a);
R=min(R,(double)(w-x)/a);
}else{
L=max(L,(double)(w-x)/a);
R=min(R,-(double)x/a);
}
}
const int MAXN=100000+10;
struct Event{
double x;int type;
bool operator < (const Event& a) const{return x<a.x||(x==a.x&&type>a.type);}
}event[MAXN*2];
int main(){
int T;
scanf("%d",&T);
while(T--){
int w,h,n,tail=0;
scanf("%d%d%d",&w,&h,&n);
for(register int i=1;i<=n;i++){
int x,y,a,b;
scanf("%d%d%d%d",&x,&y,&a,&b);
double L=0,R=1e9;
update(x,a,w,L,R);
update(y,b,h,L,R);
if(R>L){
event[++tail]=(Event){L,0};
event[++tail]=(Event){R,1};
}
}
sort(event+1,event+tail+1);
int cnt=0,ans=0;
for(register int i=1;i<=tail;i++){
if(event[i].type==0) ans=max(ans,++cnt);
else cnt--;
}
printf("%d\n",ans);
}
return 0;
}
Jurassic Remains,NEERC 2003,LA 2965 题解
Vjudge传送门
题意:
大概的题意是指现在有n个由大写字母组成的字符串,选择尽量多的穿,是的每个大写字母都出现了偶数次
题解:
最开始我想的就是bitset暴力,说不定还能卡过,因为(1<=n<=24),所以我们直接bitset说不定也是可以的//直接穷举应该就可以了233.复杂度是
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<map>
using namespace std;
map<int,int>mp;
const int MAXN=100+10;
int bitcount(int x){
int cnt=0;
while(x){if(x&1!=0) cnt++; x>>=1;}
return cnt;
}
int main(){
int n,A[MAXN];
char s[1000];
while(scanf("%d",&n)==1&&n){
for(register int i=0;i<=n-1;i++){
scanf("%s",s);
A[i]=0;
for(register int j=0;s[j]!='\0';j++) A[i]^=(1<<(s[j]-'A'));
}
mp.clear();
int n1=n/2,n2=n-n1;
for(register int i=0;i<(1<<n1);i++){
int x=0;//表示一种集合的异或和
for(register int j=0;j<n1;j++) if(i&(1<<j)) x^=A[j];
if(!mp.count(x)||bitcount(mp[x])<bitcount(i)) mp[x]=i;
}
int ans=0;
for(register int i=0;i<(1<<n2);i++){
int x=0;
for(register int j=0;j<n2;j++) if(i&(1<<j))x^=A[n1+j];
if(mp.count(x)&&bitcount(ans)<bitcount(mp[x])+bitcount(i)) ans=(i<<n1)^mp[x];
}
printf("%d\n",bitcount(ans));
for(register int i=0;i<n;i++) if(ans&(1<<i)) printf("%d ",i+1);
printf("\n");
}
return 0;
}
Prince ans Princess,UVa 10635题解
Vjudge传送门
题意:
现在有两条长度为p+1和q+1的序列,每个序列的元素都各不相同,都是
1
到
题解:
LCS我貌似只会pq的写法,显然是要挂的(尤其是ccf老年机),所以我们想一下其他解法来解LCS,显然这道题有一个性质,也就是无论是第一条序列还是第二条序列,他们的元素都是互异的,因此我们的思路是先给A数组重新编号,从1开始,一直编到A的长度,对于B数组,如果这个位置的数在A中出现过,那么就应该是这个数在A中出现的位置的值,如果没有出现过就是0,然后我们求一下B的LIS就可以了,LIS貌似我们是可以nlogn做的,所以这道题就迎刃而解了。
其实刚才说的这个道理是很清楚的,因为我们把所有的B数组都变成了A数组中该数字出现的位置,那么如果找出一个连续递增的B数组,一定可以找到A和B的LCS为B的LIS所表示的这个LCS,也即B中找到的序列,在A中的位置要递增,显然吧如果在B中位置是递增的但是在A中位置不递增,怎么LCS?所以这道题就这么转化啦,非常愉快
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=250*250+10;
const int INF=1000000000;
int a[MAXN],b[MAXN],c[MAXN],num[MAXN],l[MAXN],g[MAXN],T,tail;
int main(){
scanf("%d",&T);
for(register int kase=1;kase<=T;kase++){
int N,p,q,x;
scanf("%d%d%d",&N,&p,&q);
memset(c,0,sizeof(c));
for(register int i=1;i<=p+1;i++) scanf("%d",&a[i]),c[a[i]]=i;
for(register int i=1;i<=q+1;i++){
scanf("%d",&b[i]),b[i]=c[b[i]];
if(b[i]!=0) num[++tail]=b[i];
}
int ans=0;
for(register int i=1;i<=tail;i++) g[i]=INF;
for(register int i=1;i<=tail;i++){
int k=lower_bound(g+1,g+tail+1,num[i])-g;
l[i]=k;g[k]=num[i];
ans=max(ans,l[i]);
}
printf("Case %d: %d\n",kase,ans);
}
return 0;
}
/*
1
3 6 7
1 7 5 4 8 3 9
1 4 3 5 6 2 8 9
*/
Game of Sum,UVa 10891题解
题意:
有一个长度为n的序列,然后这个序列的元素可正可负,现在要A和B轮流取数,每次可以取左边或者右边的任意多个数,当然你可以在某一次把这个序列直接取完,A先取,求A的得分减去B的得分后的结果
题解:
显然这道题如果问的不是是A取的分减去B取的分而是A取的分加上B取的分的话,会好做得多(这不是废话嘛)…
也就是说,A和B取的分的和是一定的,就看谁取得多,谁取的少,但是总和是一定的,所以到这里我们大概已经想到了转移的方法,另一个dp[i][j]表示i到j这个区间,先手能够获得的最大收益,那么显然我们可以初始化dp数组为dp[i][i]=a[i],然后我们会发现每次转移的时候,我们可以枚举一下i和j中间的点,使得前一段先手的人拿然后留后一段给另一个人先手,或者后一段先手的人拿留前一段给另一个人先手,所以我们可以推出
然后我们每次枚举转移就可以了,但是据说还有一种可以优化到 n2 的方法,就是记一个f数组和一个g数组,分别表示dp数组前一段区间的最小值和dp数组后一段区间的最小值,然后转移就变成了O(1)的,不过貌似不是很必要,但是很优美是承认的233
代码:
#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=100+10;
int n,b[MAXN],a[MAXN],dp[MAXN][MAXN];
//4
//4 -10 -20 7
int main(){
while(scanf("%d",&n)==1&&n){
for(int i=1;i<=105;i++){
for(int j=1;j<=105;j++){
dp[i][j]=-100000000;
}
}
b[0]=0;
for(register int i=1;i<=n;i++){scanf("%d",&a[i]);dp[i][i]=a[i];b[i]=b[i-1]+a[i];}
for(register int len=2;len<=n;len++){
for(register int i=1;i<=n-len+1;i++){
int j=i+len-1;
for(register int k=i+1;k<=j;k++){dp[i][j]=max(dp[i][j],b[j]-b[i-1]-dp[k][j]);}
dp[i][j]=max(dp[i][j],b[j]-b[i-1]);
for(register int k=i;k<j;k++)dp[i][j]=max(dp[i][j],b[j]-b[i-1]-dp[i][k]);
}
}
printf("%d\n",2*dp[1][n]-b[n]);
}
return 0;
}
Calculator Conundrum,UVa 11549题解
题意&题解:
有一个神奇的计算器,然后每次计算之后只显示前n位,比如n为5的时候2147483647显示21474,然后我们只需要处理一下long long然后转字符串然后再转回来就行了,然后突然发现可以直接除啊…我是不是傻…竟然copy了刘汝佳的代码
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int buf[10],T,n,k;
int next(int n,int k){
if(!k) return 0;
long long k2=(long long)k*k;
int L=0;
while(k2>0){buf[L++]=k2%10;k2/=10;}
if(n>L) n=L;
int ans=0;
for(register int i=0;i<n;i++) ans=ans*10+buf[--L];
return ans;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
int ans=k;
int k1=k,k2=k;
do{
k1=next(n,k1);
k2=next(n,k2);if(k2>ans) ans=k2;
k2=next(n,k2);if(k2>ans) ans=k2;
}while(k1!=k2);
printf("%d\n",ans);
}
return 0;
}