Link:
A:
求原根的个数,有一条性质是原根个数为$phi(phi(n))$,多了一个不会证的性质
如果要确定哪些是原根的话还是要枚举,不过对于每个数不用枚举$p$次了
由于$delta_p(x) | phi(x)$,只要对欧拉函数值的约数枚举即可
不过此题好像直接$O(p^2)$枚举就行了……
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; typedef double db; int p,res; int main() { scanf("%d",&p); for(int i=1;i<p;i++) { bool f=1;int t=1; for(int j=1;j<=p-2;j++) { t=(t*i)%p; if(t==1) f=0; } if(t*i%p!=1) f=0; res+=f; } printf("%d",res); return 0; }
B:
简单分类
#include <bits/stdc++.h> using namespace std; int n,A,I;char s[200005]; int main() { scanf("%d%s",&n,s+1); for(int i=1;i<=n;i++) if(s[i]=='A') A++; else if(s[i]=='I') I++; if(!I) printf("%d",A); else if(I==1) printf("1"); else printf("0"); return 0; }
C:
明明写起来很简单的一道题目被我弄复杂了
考虑维护序列中原来的值和增量
由于只要求当前最后一个数的增量,因此可以每次仅在增加的最后一个数打上标记
这样在删除数时将当前末尾的增量向前推就能满足要求
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> P; const int MAXN=2e5+10; int n,det[MAXN],dat[MAXN],lst;double sum; int main() { scanf("%d",&n); dat[1]=0;lst=1; while(n--) { int op,x,y; scanf("%d",&op); if(op==1) scanf("%d%d",&x,&y),sum+=x*y,det[x]+=y; else if(op==2) scanf("%d",&x),sum+=x,dat[++lst]=x; else sum-=dat[lst]+det[lst],det[lst-1]+=det[lst],det[lst--]=0; printf("%.6lf ",sum/lst); } return 0; }
结果我这个zz用树状数组维护了这个东西,还将一个增量写成了赋值WA了好几次……
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=2e5+10; int n;ll bit[MAXN<<2],dat[MAXN<<2],lst,sum; void Update(int pos,int x) {while(pos<=n) bit[pos]+=x,pos+=pos&(-pos);} ll Query(int pos) {ll ret=0;while(pos) ret+=bit[pos],pos-=pos&(-pos);return ret;} int main() { scanf("%d",&n); lst=1;sum=0; for(int i=1;i<=n;i++) { int op,x,y; scanf("%d",&op); if(op==1) { scanf("%d%d",&x,&y); Update(1,y);Update(x+1,-y); dat[1]+=y;dat[x+1]-=y; sum+=x*y; } else if(op==2) { scanf("%d",&x); Update(lst+1,x);Update(lst+2,-x); dat[++lst]+=x;dat[lst+1]=-x; sum+=x; } else { sum-=Query(lst); Update(lst+1,-dat[lst+1]); Update(lst,dat[lst+1]); dat[lst]+=dat[lst+1];dat[lst+1]=0;lst--; } printf("%.6lf ",1.0*sum/lst); } return 0; }
D:
可以发现真正的状态数只有$2*n$个,对于每一位只有加/减两种状态
发现$y$的值就是走过的总距离,这样用$dp[n][2]$记忆化搜索在当前状态走到边界有多远
每个状态在入栈时打上标记,如果再次走到形成环就是循环了
这样对于每个$i$就是对应于$dp[i+1][0]$时的解
注意将边界$dp[1][0/1]$设为-1
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=2e5+10; int n,dat[MAXN],vis[MAXN][2];ll dp[MAXN][2]; ll dfs(int x,int d) { if(x<=0||x>n) return 0; if(vis[x][d]==1) return -1; if(vis[x][d]==2) return dp[x][d]; vis[x][d]=1; ll dist=d?dfs(x+dat[x],0):dfs(x-dat[x],1); vis[x][d]=2; if(dist==-1) return dp[x][d]=-1; else return dp[x][d]=dist+dat[x]; } int main() { scanf("%d",&n); for(int i=2;i<=n;i++) scanf("%d",&dat[i]); vis[1][0]=vis[1][1]=1; for(int i=2;i<=n;i++) { if(!vis[i][0]) dfs(i,0); printf("%I64d ",dp[i][0]==-1?-1:i+dp[i][0]-1); } return 0; }
E:
由于每个点只有一个后继,因此最终图只有环和链
将环的情况排除后就只剩链了
对于一条链,一个可行解为$a_1*w_1+a_2*w_2...+a_n*w_n(a_1>a_2>...>a_n)$
但由于后面的限制条件不能直接背包,考虑将式子变换后消除限制:
设$d_i=a_i-a_{i-1},suf_i=w_i+w_{i+1}...+w_n$,
这样式子变为$d_1*suf_1+d_2*suf_2...+d_n*suf_n$
发现就是一个完全背包,且可以将各个链合并在一起考虑
但注意,这个背包要求$d_1,d_2...d_{n-1}>0$,否则无法保证条件成立
也就是原式每个物品的个数要达到${n-1,n-2...2,1,0}$的下限!
这样就先都取一个,并将其从$t$中减去再跑背包即可
#include <bits/stdc++.h> using namespace std; #define X first #define Y second typedef long long ll; typedef pair<int,int> P; const int MAXN=1e5+10,MOD=1e9+7; int n,q,t,x,y,a[MAXN],nxt[MAXN],d[MAXN],dp[MAXN],cnt; int main() { scanf("%d%d%d",&n,&q,&t); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=q;i++) scanf("%d%d",&x,&y),nxt[x]=y,d[y]++; for(int i=1;i<=n&&t>=0;i++) { if(d[i]) continue; int sum=0;//t<0一定要及时退出! for(int j=i;j&&t>=0;j=nxt[j]) { cnt++; sum+=a[j];a[j]=sum; if(nxt[j]) t-=a[j]; } } if(cnt<n||t<0) return puts("0"),0; dp[0]=1; for(int i=1;i<=n;i++) for(int j=a[i];j<=t;j++) (dp[j]+=dp[j-a[i]])%=MOD; printf("%d",dp[t]); return 0; }
同时还有一个注意点:$t<0$时要及时退出否则会爆$int$