训练实况
<Dashboard - CCPC Jinan Test - Codeforces>
\(C\)
对于构造指令使得它能执行若干条指令这种问题,我们确实挺容易想到将其拆分为 \(+1\) 以及 \(\times 2\) 来解决。
问题就在于如何转化为这个问题。
对于这种类似于图的推进结构,最简单的永远是链。
我们发现要使栈为空,最简单的方式是从头到尾走两遍。
至此我们就有了整个问题的基础结构。
现在的问题就是在只增加一个指令的情况下完成 \(\times 2\) 以及 \(+1\) 。
\(\times 2\) 便是让其回头一次。
\(+1\) 便是让链变长一点。
#include<cstdio>
const int N=666;
struct node{
int a,b,c,d;
}t[N];
int tp,st[N];
int main(){
int n;
scanf("%d",&n);
if(n==1){
printf("1\nHALT; PUSH 127 GOTO 1");
return 0;
}
n=(n-1)/2;
while(n){
st[++tp]=n&1;
n>>=1;
}
int tot=0;
t[++tot]={1,2,1,1};
for(int i=tp-1;i>=1;i--){
t[tot]={tot,tot+1,tot,1};
tot++;
t[tot]={tot,tot+1,tot,tot};
if(st[i]){
tot++;
t[tot]={tot,tot+1,tot,tot};
}
}
printf("%d\n",tot+1);
for(int i=1;i<=tot;i++)printf("POP %d GOTO %d; PUSH %d GOTO %d\n",t[i].a,t[i].b,t[i].c,t[i].d);
printf("HALT; PUSH 127 GOTO 1");
return 0;
}
\(D\)
事实上,如果对 \(f_{u,v}=(cnt,w)\) 表示在栈顶为 \(u\) 的时候进入指令 \(v\) 后弹出 \(u\) 并进入 \(w\) 指令需要的次数,它的状态数是 \(O(n^2)\) 的。
#include<cstdio>
const int N=1055;
int n;
struct go{
int a,b,c,d;
}tt[N];
struct node{
int a,b;
}f[N][N];
int cnt=0,flag=1;
const int maxn=1024*1024+111;
const int mod=998244353;
node dfs(int u,int v){
if(++cnt>=maxn)flag=0;
if(!flag)return {0,0};
if(f[u][v].a)return f[u][v];
int top=tt[v].c,go=tt[v].d,ans=0;
if(u==0&&tt[v].a==0)return f[u][v]={1,0};
if(tt[v].a==u)return f[u][v]={1,tt[v].b};
node now1=dfs(tt[v].c,tt[v].d);
if(now1.b==0)return f[u][v]={now1.a+1,0};
else {
node now2=dfs(u,now1.b);
return f[u][v]={(now1.a+now2.a+1)%mod,now2.b};
}
}
char s[10];
int main(){
// freopen("a.in","r",stdin);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s);
if(s[0]=='P'){
scanf("%d GOTO %d; PUSH %d GOTO %d",&tt[i].a,&tt[i].b,&tt[i].c,&tt[i].d);
}
else {
scanf(" PUSH %d GOTO %d",&tt[i].c,&tt[i].d);
}
}
int ans=dfs(0,1).a;
if(flag)printf("%d\n",ans%mod);
else puts("-1");
return 0;
}
\(H\)
此题略微抽象。
首先考虑一位会受到什么影响。
它只会受到前一位如果进位 \(+1\) 的影响。
但如果要考虑前一位是否能 \(+1\) 就又得去枚举更前面的顺序,但我们不关心他如何能 \(+1\) 。
我们仔细思考后发现,如果前一位能自由选择,那么自由选择的时候一定能通过改变若干顺序,来不改变前面的值来达到目的。
因此我们设立三种状态 \(0,1,2\) ,分别表示一定要进位,不一定要进位,能自由选择进位与否。
再套上数位 \(dp\) 常用套路即可。
#include<cstdio>
#include<cstring>
const int M=19;
int s1[M+2],s2[M+2];
long long f[M+2][2][3];
void work(){
long long x,y;
int tp1=0,tp2=0;
scanf("%lld%lld",&x,&y);
for(int i=1;i<=M;i++)s1[i]=s2[i]=0;
while(x)s1[++tp1]=x%10,x/=10;
while(y)s2[++tp2]=y%10,y/=10;
memset(f,0,sizeof f);
f[0][0][0]=1;
for(int i=1;i<=M;i++){
if(s1[i]==0){
for(int k=0;k<=s2[i];k++){
if(k<=3){
f[i][0][0]+=f[i-1][0][0];
f[i][0][0]+=f[i-1][0][1];
f[i][0][0]+=f[i-1][0][2];
}
else if(k==4){
f[i][0][0]+=f[i-1][0][0];
f[i][0][2]+=f[i-1][0][1];
f[i][0][2]+=f[i-1][0][2];
}
else {
f[i][0][1]+=f[i-1][0][0];
f[i][0][1]+=f[i-1][0][1];
f[i][0][1]+=f[i-1][0][2];
}
}
for(int k=0;k<s2[i];k++){
if(k<=3){
f[i][0][0]+=f[i-1][1][0];
f[i][0][0]+=f[i-1][1][1];
f[i][0][0]+=f[i-1][1][2];
}
else if(k==4){
f[i][0][0]+=f[i-1][1][0];
f[i][0][2]+=f[i-1][1][1];
f[i][0][2]+=f[i-1][1][2];
}
else {
f[i][0][1]+=f[i-1][1][0];
f[i][0][1]+=f[i-1][1][1];
f[i][0][1]+=f[i-1][1][2];
}
}
for(int k=s2[i]+1;k<=9;k++){
if(k<=3){
f[i][1][0]+=f[i-1][0][0];
f[i][1][0]+=f[i-1][0][1];
f[i][1][0]+=f[i-1][0][2];
}
else if(k==4){
f[i][1][0]+=f[i-1][0][0];
f[i][1][2]+=f[i-1][0][1];
f[i][1][2]+=f[i-1][0][2];
}
else {
f[i][1][1]+=f[i-1][0][0];
f[i][1][1]+=f[i-1][0][1];
f[i][1][1]+=f[i-1][0][2];
}
}
for(int k=s2[i];k<=9;k++){
if(k<=3){
f[i][1][0]+=f[i-1][1][0];
f[i][1][0]+=f[i-1][1][1];
f[i][1][0]+=f[i-1][1][2];
}
else if(k==4){
f[i][1][0]+=f[i-1][1][0];
f[i][1][2]+=f[i-1][1][1];
f[i][1][2]+=f[i-1][1][2];
}
else {
f[i][1][1]+=f[i-1][1][0];
f[i][1][1]+=f[i-1][1][1];
f[i][1][1]+=f[i-1][1][2];
}
}
}
else if(s1[i]==1){
for(int k=0;k<=s2[i];k++){
if(k==1){
f[i][0][0]+=f[i-1][0][0];
f[i][0][0]+=f[i-1][0][1];
f[i][0][0]+=f[i-1][0][2];
}
else if(k<=4){
f[i][0][0]+=f[i-1][0][1];
f[i][0][0]+=f[i-1][0][2];
}
else {
f[i][0][1]+=f[i-1][0][1];
f[i][0][1]+=f[i-1][0][2];
}
}
for(int k=0;k<s2[i];k++){
if(k==1){
f[i][0][0]+=f[i-1][1][0];
f[i][0][0]+=f[i-1][1][1];
f[i][0][0]+=f[i-1][1][2];
}
else if(k<=4){
f[i][0][0]+=f[i-1][1][1];
f[i][0][0]+=f[i-1][1][2];
}
else {
f[i][0][1]+=f[i-1][1][1];
f[i][0][1]+=f[i-1][1][2];
}
}
for(int k=s2[i]+1;k<=9;k++){
if(k==1){
f[i][1][0]+=f[i-1][0][0];
f[i][1][0]+=f[i-1][0][1];
f[i][1][0]+=f[i-1][0][2];
}
else if(k<=4){
f[i][1][0]+=f[i-1][0][1];
f[i][1][0]+=f[i-1][0][2];
}
else {
f[i][1][1]+=f[i-1][0][1];
f[i][1][1]+=f[i-1][0][2];
}
}
for(int k=s2[i];k<=9;k++){
if(k==1){
f[i][1][0]+=f[i-1][1][0];
f[i][1][0]+=f[i-1][1][1];
f[i][1][0]+=f[i-1][1][2];
}
else if(k<=4){
f[i][1][0]+=f[i-1][1][1];
f[i][1][0]+=f[i-1][1][2];
}
else {
f[i][1][1]+=f[i-1][1][1];
f[i][1][1]+=f[i-1][1][2];
}
}
}
else {
for(int k=0;k<=s2[i];k++){
if(k==s1[i]-1){
f[i][0][0]+=f[i-1][0][1];
f[i][0][0]+=f[i-1][0][2];
}
else if(k==s1[i]){
f[i][0][0]+=f[i-1][0][0];
f[i][0][0]+=f[i-1][0][2];
}
}
for(int k=0;k<s2[i];k++){
if(k==s1[i]-1){
f[i][0][0]+=f[i-1][1][1];
f[i][0][0]+=f[i-1][1][2];
}
else if(k==s1[i]){
f[i][0][0]+=f[i-1][1][0];
f[i][0][0]+=f[i-1][1][2];
}
}
for(int k=s2[i]+1;k<=9;k++){
if(k==s1[i]-1){
f[i][1][0]+=f[i-1][0][1];
f[i][1][0]+=f[i-1][0][2];
}
else if(k==s1[i]){
f[i][1][0]+=f[i-1][0][0];
f[i][1][0]+=f[i-1][0][2];
}
}
for(int k=s2[i];k<=9;k++){
if(k==s1[i]-1){
f[i][1][0]+=f[i-1][1][1];
f[i][1][0]+=f[i-1][1][2];
}
else if(k==s1[i]){
f[i][1][0]+=f[i-1][1][0];
f[i][1][0]+=f[i-1][1][2];
}
}
}
}
printf("%lld\n",f[M][0][0]);
}
int main(){
// freopen("a.in","r",stdin);
int t;
scanf("%d",&t);
while(t--)work();
return 0;
}
\(F\)
问题可以转化为需要删去的个数。
假定目前考虑的所有数最小值为 \(x\) 。
那么只有在集合里全是 \(x\) 的倍数时需要删。
当然,有可能得删多次。
直接搜索即可。
#include<cstdio>
const int N=100010,M=100000;
const int mod=998244353;
int ksm(int x,int y){
int ans=1;
while(y){
if(y&1)ans=1ll*ans*x%mod;
x=1ll*x*x%mod;
y>>=1;
}
return ans;
}
int pre[N],ipre[N];
void init(){
pre[0]=1;
for(int i=1;i<=M;i++)pre[i]=1ll*pre[i-1]*i%mod;
ipre[M]=ksm(pre[M],mod-2);
for(int i=M;i>=1;i--)ipre[i-1]=1ll*ipre[i]*i%mod;
}
int C(int x,int y){
if(y>x)return 0;
return 1ll*pre[x]*ipre[x-y]%mod*ipre[y]%mod;
}
int n,m;
int dfs(int x,int y){
if(1ll*x*y>m)return 0;
if(y==1)return 1;
int ans=C(m/x-1,y-1);
for(int k=2;k*x<=m;k++){
ans=(ans+dfs(k*x,y-1))%mod;
}
return ans;
}
int main(){
init();
scanf("%d%d",&m,&n);
int ans=0;
for(int i=1;i*n<=m;i++){
if(i*n<=m)ans=(ans+dfs(i,n))%mod;
}
printf("%d\n",(1ll*C(m,n)*n%mod+mod-ans)%mod);
return 0;
}
\(D\)
还是忽略了一种情况。
尽可能往下界靠。
还是一样的,小情况构造,大情况类似。
#include<cstdio>
void work(){
int n;
scanf("%d",&n);
int flag=-1;
for(int i=1;i+2<=n;i+=2){
if(n%2==1&&i==n-4){
break;
}
int x,y;
printf("? %d %d\n",i,i+1);
fflush(stdout);
scanf("%d",&x);
printf("? %d %d\n",i+1,i);
fflush(stdout);
scanf("%d",&y);
if(x!=y){
flag=i;
break;
}
}
if(flag==-1&&n%2==1){
// printf("! %d\n",n);
if(n==3){
printf("! %d\n",3);
fflush(stdout);
return ;
}
int x,y,z;
printf("? %d %d\n",n-4,n-3);
fflush(stdout);
scanf("%d",&x);
printf("? %d %d\n",n-3,n-2);
fflush(stdout);
scanf("%d",&y);
printf("? %d %d\n",n-2,n-4);
fflush(stdout);
scanf("%d",&z);
if(x+y+z==0||x+y+z==2){
int u,v;
printf("? %d %d\n",n-4,n-2);
fflush(stdout);
scanf("%d",&u);
printf("? %d %d\n",n-3,n-4);
fflush(stdout);
scanf("%d",&v);
int ans;
if(u!=z&&v!=x)ans=n-4;
else if(u==z&&v!=x)ans=n-3;
else ans=n-2;
printf("! %d\n",ans);
fflush(stdout);
return ;
}
else {
flag=n-1;
}
// return ;
}
if(flag==-1)flag=n-1;
int id;
if(flag==1)id=n;
else id=1;
int x,y;
printf("? %d %d\n",flag,id);
fflush(stdout);
scanf("%d",&x);
printf("? %d %d\n",id,flag);
fflush(stdout);
scanf("%d",&y);
if(x==y){
printf("! %d\n",flag+1);
fflush(stdout);
return ;
}
else {
printf("! %d\n",flag);
fflush(stdout);
return ;
}
}
int main(){
int t;
scanf("%d",&t);
while(t--)work();
return 0;
}
\(E\)
考虑拆到行和列上
#include<cstdio>
#include<vector>
const int N=200020;
const int mod=1e9+7;
int f[N],val[N],ans[N];
int n,m,k,q;
int find(int x){
while(x!=f[x])x=f[x]=f[f[x]];
return x;
}
std::vector<int>t[N];
void work(){
scanf("%d%d%d%d",&n,&m,&k,&q);
for(int i=1;i<=n+m;i++)f[i]=i,val[i]=0,t[i].clear(),t[i].push_back(i);
int now=n+m-1,all=0;
if(k==0)printf("%d\n",ans[now]);
for(int i=1,u,v,w;i<=k+q;i++){
scanf("%d%d%d",&u,&v,&w);
v+=n;
int x=find(u),y=find(v);
if(x==y){
// puts("yes");
// printf("%d %d %d %d %d\n",u,v,w,val[u],val[v]);
if((val[u]^val[v])!=w){
now=-1;
}
}
else {
now--;
if(t[x].size()<t[y].size()){
int tot=val[u]^val[v]^w;
for(int j:t[x])t[y].push_back(j),val[j]^=tot;
// printf("%d %d %d %d %d %d == \n",val[u],val[v],val[w],u,v,w);
f[x]=y;
}
else {
int tot=val[u]^val[v]^w;
for(int j:t[y])t[x].push_back(j),val[j]^=tot;
// printf("%d %d %d %d %d %d == \n",val[u],val[v],val[w],u,v,w);
f[y]=x;
}
}
if(i>=k){
if(now>=0)printf("%d\n",ans[now]);
else printf("%d\n",0);
}
// printf("%d %d %d\n",now,i,find(x));
// for(int j=1;j<=n+m;j++)printf("%d ",val[j]);
// puts("");
}
}
int main(){
// freopen("a.in","r",stdin);
int t;
scanf("%d",&t);
ans[0]=1,ans[1]=(1<<30)%mod;
for(int i=2;i<=200000;i++)ans[i]=1ll*ans[i-1]*ans[1]%mod;
while(t--)work();
return 0;
}
首先需要机制的猜想不会连续交换
#include<cstdio>
const int N=200020;
int f[N][2];
int n,a[N];
int min(int x,int y){
return x<y?x:y;
}
const int inf=100000009;
void work(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",a+i);
}
f[1][0]=f[1][1]=1;
for(int i=2;i<=n;i++){
f[i][0]=min(f[i-1][1]+1-(a[i]==a[i-2]),f[i-1][0]+1-(a[i]==a[i-1]));
f[i][1]=min(f[i-2][0]+1-(a[i]==a[i-2])+1,i>=3?f[i-2][1]+1-(a[i]==a[i-3])+1:inf)+1;
}
printf("%d\n",min(f[n][0],f[n][1]));
}
int main(){
int t;
scanf("%d",&t);
while(t--)work();
return 0;
}
Codeforces Round 1012 (Div. 1)
\(B\)
那我问你
如果正常做有循环
那你就不能考虑它没有循环的情况?
可以找一下最不可能有循环的地方
最不可能被影响到的地方
这不一定需要只通过脑袋想
这还可以通过样例来模拟
就比如这题
sum和最小的地方的后一位或许是个好地方
还有 \(rotate\) 这个函数或许值得参考
还有一些函数写在主程序里的做法

浙公网安备 33010602011771号