正题
T1:序言页码 PrefaceNumberingPreface NumberingPrefaceNumbering
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1465
题目大意
求1∼n1\sim n1∼n的罗马数字每个字母出现的次数。
解题思路
先写一个表,表示这个位上出现哪个数字表示10n10n10n的字母出现次数和表示5∗10n5*10n5∗10n的字母出现的次数。
然后直接计算
code
#include<cstdio>
#include<string>
using namespace std;
const char f[8]={' ','I','V','X','L','C','D','M'};
const int I[11]={0,1,2,3,1,0,1,2,3,1};
const int V[11]={0,0,0,0,1,1,1,1,1,0};
int n,c[8];
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){c[1]+=I[i%10];c[2]+=V[i%10];c[3]+=I[i%100/10]+(i%10==9);c[4]+=V[i%100/10];c[5]+=I[i%1000/100]+(i%100/10==9);c[6]+=V[i%1000/100];c[7]+=I[i/1000]+(i%1000/100==9);}for(int i=1;i<=7;i++)if(c[i]) printf("%c %d\n",f[i],c[i]);
}
T2:集合 SubsetSumsSubset SumsSubsetSums
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1466
题目大意
求将1∼n1\sim n1∼n分解为两组且两组之和相等的方案数。
解题思路
用fi,jf_{i,j}fi,j表示前i个数,两组相差为j时的方案总数。
然后
fi,j=fi,∣j−i∣+fi,j+if_{i,j}=f_{i,|j-i|}+f_{i,j+i}fi,j=fi,∣j−i∣+fi,j+i
然后得出的答案要除2,因为两组是一样的。
code
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
ll n,sum,f[40][801];
int main()
{scanf("%lld",&n);sum=0;f[0][0]=1;for(ll i=1;i<=n;i++){sum+=i;for(ll j=0;j<=sum;j++){f[i][j]=f[i-1][abs(j-i)]+f[i-1][j+i];}}printf("%lld",f[n][0]/2);
}
T3:循环数 RunaroundNumbersRunaround NumbersRunaroundNumbers
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1467
题目大意
求比n大的第一个循环数(循环数就是每到达一个数字就往前走这个数字的步数,然后每个数字都走过一次)
解题思路
直接暴力往后加,然后看一下是不是循环数。
code
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int n,now,k[10],a[10];
bool check(int x)
{memset(k,0,sizeof(k));int tmp=x,w=0;while(tmp){w++;a[w]=tmp%10;if(k[a[w]]) return false;k[a[w]]=true;tmp/=10;}for(int i=1;i<=w/2;i++)swap(a[i],a[w-i+1]);memset(k,0,sizeof(k));tmp=1;for(int i=1;i<=w;i++){if(k[a[tmp]]||!a[tmp]) return false;k[a[tmp]]=true;tmp=(tmp+a[tmp]-1)%w+1;}if(tmp!=1) return false;return true;
}//判断循环数
int main()
{scanf("%d",&n);now=n;while(true){now++;if(check(now)){printf("%d",now);break;}}
}
T4:派对灯 PartyLampsParty LampsPartyLamps
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P1468
题目大意
有n个灯
有4种操作:
1.灯开着就关,关了就开
2.奇数号的取反
3.偶数号的取反
4.3k+1的灯取反
给出操作次数和某些灯的最终状态,求所有可能的状态
解题思路
我们可以发现其实灯就是6个一个的循环节。
然后一个东西操作过2次就没有用,然后1和2,3就等于按另一个
我们可以预处理除c=2可以做到的所有情况,然后c>2时也可以做到c=2时的效果
code
#include<cstdio>
using namespace std;
const int s[8]={0,14,21,27,36,42,49,63};//预处理可以做到的情况
int n,c,open,close,a[6],x;
bool flag;
void check(int x)
{if((s[x]&open)!=open||s[x]&close) return;int tmp=s[x],i=1;while(tmp) a[6-i]=tmp%2,tmp/=2,i++;for(int i=0;i<n;i++)printf("%d",a[i%6]);flag=true;printf("\n");
}//判断这种情况是否满足
int main()
{scanf("%d%d",&n,&c);while(scanf("%d",&x)&&x!=-1)x=6-(x-1)%6-1,open|=1<<x;//开启的等while(scanf("%d",&x)&&x!=-1)x=6-(x-1)%6-1,close|=1<<x;//关闭的灯if(c==0) check(7);if(c==1) check(0),check(2),check(3),check(5);//前两个特判if(c>=2) for(int i=0;i<=7;i++) check(i);if(!flag) printf("IMPOSSIBLE");
}