T1
题意:给你一个 (n) 个点 (n) 条边的有向图,
求每个店经过 (K) 条边后的边权和、最小边权
(Kle 10^{10})
考试时:一直想着环,结果一直不知道怎么做
正解:倍增预处理出经过 (2^r) 条边的终点和最大值、最小值
然后直接查询即可
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=100005;
int n,up,to[N][40],mn[N][40],mnn;
LL sm[N][40],K,sum,tmp;
int main() {
scanf("%d%lld",&n,&K),up=log2(K)+1;
for(int i=0;i<n;i++)scanf("%d",&to[i][0]);
for(int i=0;i<n;i++)scanf("%d",&mn[i][0]),sm[i][0]=1LL*mn[i][0];
for(int j=1;j<=up;j++)
for(int i=0;i<n;i++) {
to[i][j]=to[to[i][j-1]][j-1];
mn[i][j]=min(mn[i][j-1],mn[to[i][j-1]][j-1]);
sm[i][j]=sm[i][j-1]+sm[to[i][j-1]][j-1];
}
for(int i=0,u;i<n;i++) {
u=i,tmp=K,sum=0,mnn=INT_MAX;
for(int j=up;~j;j--) {
if(tmp>=(1ll<<j)) {
sum+=sm[u][j];
mnn=min(mnn,mn[u][j]);
u=to[u][j];
tmp-=(1ll<<j);
}
}
printf("%lld %d
",sum,mnn);
}
}
T2
题意:求 (n) 所有的原根,如果 (Ord_n(a)=varphi(n)) 则 (a) 是 (n) 的原根
其中 (Ord_n(a)) 是满足 (a^dequiv 1pmod n) 的最小的 (d)
比赛时:直接暴力,但是怕超时改了范围,结果 Wa
#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b) {
return b?gcd(b,a%b):a;
}
int n,rh,fl;
inline int Ord(int a) {
register int b=1;
for(int i=1;i<=rh;i++) {
b=b*a%n;
if(b==1%n)return i;
}
return -1;
}
int main() {
freopen("math.in","r",stdin);
freopen("math.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
rh+=(gcd(i,n)==1);
for(int i=1;i<=n;i++)
if(gcd(i,n)==1 && Ord(i)==rh)
printf("%d
",i),fl=1;
if(!fl)puts("-1");
}
T3
题意:给你一颗有根树,保证一个点除了根节点以外的点都有唯一父亲
问有多少棵子树满足里面的结点是个连续的区间
考试:记录子树的最大值和最小值,然后用判断个数是否等于 (mx-mn+1)
正解:就是如此
细节炸了
#include<bits/stdc++.h>
using namespace std;
const int N=100005;
int n,ans,sz[N],mn[N],mx[N],rd[N],lst[N],nxt[N<<1],to[N<<1],cnt;
inline void Ae(int fr,int go) { to[++cnt]=go,nxt[cnt]=lst[fr],lst[fr]=cnt; }
void dfs(int u,int f) {
mn[u]=mx[u]=u,sz[u]=1;
for(int i=lst[u],v;i;i=nxt[i])
if((v=to[i])^f) {
dfs(v,u);
mn[u]=min(mn[u],mn[v]);
mx[u]=max(mx[u],mx[v]);
sz[u]+=sz[v];
}
if(sz[u]==mx[u]-mn[u]+1)
++ans;
}
int main() {
scanf("%d",&n);
for(int i=1,u,v;i<n;i++) {
scanf("%d%d",&u,&v);
Ae(u,v),++rd[v];
}
for(int i=1;i<=n;i++)
if(!rd[i]) { dfs(i,i); break; }
printf("%d",ans);
}
T4
题意:一个长度为 (n) 的排列,可以用 (n-1) 的字符串表示。
对于相邻的两个数,
如果前面的数比后面的大,则这个位置是上升的,记为 I
反之,这个位置下降,记为 D
现在给你一个包含 I
, D
和 ?
的字符串,其中 ?
表示任意
问满足条件的排列个数
考试:设 (f_{i,j,k}) 表示第 (i) 位放了 (j) ,状态为 (k) 的个数
调不出来放弃
正解:设 (f_{i,j}) 为第 (i) 位放了 (j) 的个数
如果第 (i) 位是 I
,(f_{i,j}=sum_{k=1}^{j-1}f_{i-1,k})
其他的同理。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL P=1000000007;
const int N=1005;
int n,ans;
LL s[N][N],f[N][N];
char st[N];
int main() {
scanf("%s",st+1);
n=strlen(st+1)+1;
f[1][1]=s[1][1]=1;
for(int i=2;i<=n;i++) {
for(int j=1;j<=i;j++) {
if(st[i-1]=='I')f[i][j]=s[i-1][j-1]%P;
else if(st[i-1]=='D')
f[i][j]=(s[i-1][i-1]-s[i-1][j-1]+P)%P;
else f[i][j]=s[i-1][i-1]%P;
(s[i][j]=s[i][j-1]+f[i][j])%=P;
}
}
printf("%lld",s[n][n]);
}
总结
T1:对于特别大的考虑倍增、二分等 (log) 级的
T2:认真看题
T3:不要想太多
T4:状态转移需要简略、完整