总结
成绩:SOLVE:3,Penalty:4
C
签到题
void solve()
{
int n,k;
cin>>n>>k;
t-=n;
a[t]=k*t;
t++;
a[t-1]=(a[t-2]+a[t-1])%mod;
cout<<a[t-1]<<'\n';
}
H
签到题
显然,最优答案一定是参加第一场或第二场
对于每一场,只要有比自己排名高的能放到另一场的就都放到另一场
模拟取最小值
void SINGLE_TEST()
{
int n,m;
cin>>n;
map<string,PII> a,b;
int ans=INF;
vector<string> s,w;
vector<int> s1,s2,w1,w2;
int t1,t2;
int z1,z2;
map<string,bool> vis1,vis2;
for(int i=1;i<=n;i++){
string ss;cin>>ss;
int ss1,ss2;
cin>>ss1>>ss2;
s.push_back(ss);
s1.push_back(ss1);
s2.push_back(ss2);
if(ss=="lzr010506"){
t1=ss1;
t2=ss2;
}
vis1[ss]=true;
}
cin>>m;
for(int i=1;i<=m;i++){
string ss;cin>>ss;
int ss1,ss2;
cin>>ss1>>ss2;
w.push_back(ss);
w1.push_back(ss1);
w2.push_back(ss2);
if(ss=="lzr010506"){
z1=ss1;
z2=ss2;
}
vis2[ss]=true;
}
int p1=0,p2=0;
for(int i=0;i<n;i++){
if(s[i]=="lzr010506") continue;
if(s1[i]>t1 || s1[i]==t1 && s2[i]<t2){
p1++;
if(vis2[s[i]]){
p1--;
}
}
}
for(int i=0;i<m;i++){
if(w[i]=="lzr010506") continue;
if(w1[i]>z1 || w1[i]==z1 && w2[i]<z2){
p2++;
if(vis1[w[i]]){
p2--;
}
}
}
cout<<min(p1,p2)+1<<"\n";
}
A
我们可以将每个数字拆位考虑

考虑枚举个数,这数的为1
那么对于这数,固定第1位为1,剩下的m-1位,至少有一个数在这一位上为0,依次进行分配
对于每一组,方案数是
对于不选的个数,每一个数随意选择即可,每一个数的方案数为
因此答案为
值得注意,这里的模数不一定为质数,无法使用逆元,考虑到范围较小,可以预处理组合数
// 计算组合数,取模
void calculate_combinations() {
for (int n = 0; n <= MAXN; ++n) {
for (int k = 0; k <= n; ++k) {
if (k == 0 || k == n) {
comb[n][k] = 1; // C(n, 0) = C(n, n) = 1
} else {
comb[n][k] = (comb[n - 1][k - 1] + comb[n - 1][k]) % q; // 递推关系式,取模
}
}
}
}
void SINGLE_TEST()
{
int n,m;
cin>>n>>m>>q;
calculate_combinations();
ll ans=0;
for(int i=1;i<=n;i++){
ll res=get_combination(n, i) *power(power(2,i,q)-1,m-1,q);
res%=q;
res*=power(power(2,m-1,q),n-i,q);
(ans+=res)%=q;
}
cout<<ans<<"\n";
}
I
图论
对于每一个点的每一个方向进行预处理,在环上的点的答案是环的大小,一般的点的答案是走到边界的距离或到环的距离加上环的大小
对于每一个查询即可访问
难在代码处理
void dfs(int x,int y,int d){
if(x>n||x<=0||y>m||y<=0)return;
sx[++hd]=x,sy[hd]=y,sd[hd]=d;
if(p[x][y][d]){
rg=1;
return;
}
p[x][y][d]=1;
int nd=dt[d][mir[x][y]];
dfs(x+dx[nd],y+dy[nd],nd);
}
int Dfs(int x,int y,int d){
if(x>n||x<=0||y>m||y<=0)return 0;
// cerr<<x<<' '<<y<<' '<<d<<endl;
sx[++hd]=x,sy[hd]=y,sd[hd]=d;
dp[x][y][d]=0;
assert(!p[x][y][d]);
p[x][y][d]=1;
int nd=dt[d][mir[x][y]];
dp[x][y][d]+=Dfs(x+dx[nd],y+dy[nd],nd);
if(!vm[x][y]&&ok(d,mir[x][y])){
vm[x][y]=1,++dp[x][y][d];
}
return dp[x][y][d];
}
void work(){
cin>>n>>m;
memset(dp,-1,sizeof dp);
FOR(i,1,n){
cin>>s;
FOR(j,1,m){
if(s[j-1]=='-')mir[i][j]=0;
if(s[j-1]=='|')mir[i][j]=1;
if(s[j-1]=='/')mir[i][j]=2;
if(s[j-1]=='\\')mir[i][j]=3;
}
}
cin>>q;
while(q--){
int x,y,d;
cin>>x>>y>>s;
if(s=="above")d=0;
if(s=="below")d=1;
if(s=="left")d=2;
if(s=="right")d=3;
x+=dx[d],y+=dy[d];
if(x>n||x<=0||y>m||y<=0){
puts("0");
continue;
}
if(get(x,y,d)!=-1){
printf("%d\n",get(x,y,d));
continue;
}
dfs(x,y,d);
if(rg){
FOR(i,1,hd){
int X=sx[i],Y=sy[i];
if(!vm[X][Y]&&ok(sd[i],mir[X][Y])){
vm[X][Y]=1,++ans;
}
}
FOR(i,1,hd)dp[sx[i]][sy[i]][sd[i]]=ans;
clr();
printf("%d\n",get(x,y,d));
continue;
}
int X=sx[hd],Y=sy[hd],D=dt[sd[hd]][mir[X][Y]]^1;
clr();
Dfs(X,Y,D);
X=sx[hd],Y=sy[hd],D=dt[sd[hd]][mir[X][Y]]^1;
clr();
Dfs(X,Y,D);
clr();
printf("%d\n",get(x,y,d));
}
}
B
不难发现,本题的答案A题的答案只存在1个子序列为的方案数
对于1个子序列为的情况,可以反向理解为从这个序列中删掉任意一个数都无法为,
也就是每一个数至少在一个位上必须只有自己才有,这样才能确保自己不能被去掉(即对应官方题解中的“特殊位”)
因此,我们可以先为每一个数分配,然后再将多出来的进行任意分配,这样就可以得到在这一组下的方案数