总结
三道题拿的都是暴力分,和预估的基本一样
A. 环
分析
因为 (n) 不一定是质数,所以要用 (exgcd) 求逆元
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define rg register
const int maxn=1e3+5;
int t,n,k,l;
char s[maxn];
int exgcd(rg int aa,rg int bb,rg int &xx,rg int &yy){
if(bb==0){
xx=1,yy=0;
return aa;
}
rg int nans=exgcd(bb,aa%bb,xx,yy);
rg int tt=xx;
xx=yy;
yy=tt-aa/bb*yy;
return nans;
}
int gcd(rg int aa,rg int bb){
return bb==0?aa:gcd(bb,aa%bb);
}
int getny(rg int now){
rg int x,y;
exgcd(now,n,x,y);
x=(x%n+n)%n;
return x;
}
inline int mulmod(rg long long now1,rg int now2){
return now1*=now2,now1>=n?now1%n:now1;
}
int main(){
scanf("%d",&t);
while(t--){
memset(s,0,sizeof(s));
scanf("%d%d%d",&n,&k,&l);
if(gcd(n,k)!=1){
printf("NO
");
continue;
}
printf("YES
");
for(rg int i=0;i<n;i++) s[i]='0';
for(rg int i=0;i<k;i++) s[i]='0';
for(rg int i=0;i<k;i++) s[mulmod(i,getny(k))]='1';
for(rg int i=0;i<l;i++){
s[mulmod(i,getny(k))]='0';
s[(mulmod(i,getny(k))+1)%n]='1';
printf("%s
",s);
}
}
return 0;
}
B.DNA序列
分析
对于最优解是按照 (1 sim n) 的顺序连接的数据,我们考虑贪心
枚举每一个字符串有多长的前缀在最优解中,如果比当前答案更优,就更新答案
枚举的时候应该倒序枚举,而不应该正序枚举
比如
AAA
B
如果我们正着去贪心,那么对于第一个字符串,我们会选择 (A)
对于第二个字符串,我们会选择 (B)
最终的答案就是 (AB)
显然是不对的
但是如果倒着去贪心
对于第一个字符串,我们肯定在前面接得越多越好
所以会得到正确的答案 (AAAB)
这样做复杂度是 (n^4) 的,(n) 比较小,可以通过
接下来的问题就在于没有特殊性质的时候如何排序
考虑把一个字符串 (s) 分成两个部分
前半部分是一个最短的前缀 (x),满足 (x^{+infty})
后半部分是该字符串剩下的部分 (y)
比如 (AACG)
我们就可以把它写成 (A^{+infty}+CG) 的形式
这样写会有一个问题,只包含一个字母的字符串无法处理
因此要在每一个字符串后面加一个字典序较大的字母,比如 (Z)
那么 (A) 就可以写成 (A^{+infty} + Z) 的形式
前半部分肯定要按照字典序从小到大排,后半部分则需要按照字典序从大到小排
因为在前面的字符都相同的情况下
肯定要把下一个字典序较小的放在后面
前缀重复正无穷大不好处理
可以在前缀 (x) 的后面也加一个字典序较大的字母
这样排序的时候就能够把我们想要的排在前面
手模几组样例就明白了
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define rg register
const int maxn=55,maxm=1e4+5;
int n,a[maxn][maxn],ans[maxm],len[maxn],anstp,sta[maxm],tp,sum;
char s[maxn];
struct jie{
std::string x,z;
int id;
}b[maxn];
bool cmp(rg jie aa,rg jie bb){
if(aa.x==bb.x) return aa.z>bb.z;
return aa.x<bb.x;
}
void updat(){
for(rg int i=1;i<=tp;i++){
if(sta[i]>ans[i]) return;
else if(sta[i]<ans[i]) break;
}
for(rg int i=1;i<=tp;i++) ans[i]=sta[i];
anstp=tp;
for(rg int i=tp+1;i<=sum;i++) ans[i]=0;
}
void ad(rg int id,rg int nlen){
for(rg int i=tp;i>=1;i--) sta[i+nlen]=sta[i];
for(rg int i=1;i<=nlen;i++) sta[i]=a[id][i];
tp+=nlen;
}
void del(rg int nlen){
for(rg int i=1;i<=tp-nlen;i++) sta[i]=sta[i+nlen];
for(rg int i=tp-nlen+1;i<=tp;i++) sta[i]=0;
tp-=nlen;
}
int main(){
scanf("%d",&n);
std::string tmp1,tmp2,tmp3;
for(rg int i=1;i<=n;i++){
scanf("%s",s+1);
len[i]=strlen(s+1);
sum+=len[i];
tmp1.clear();
for(rg int j=1;j<=len[i];j++){
if(s[j]=='A') a[i][j]=1;
else if(s[j]=='C') a[i][j]=2;
else if(s[j]=='G') a[i][j]=3;
else a[i][j]=4;
tmp1=tmp1+s[j];
}
tmp1=tmp1+'Z';
b[i].id=i;
for(rg int j=1;j<=len[i];j++){
tmp2.clear();
for(rg int k=1;k<=j;k++) tmp2=tmp2+s[k];
tmp3=tmp2;
while(tmp3.length()<=tmp1.length()) tmp3=tmp3+tmp3;
if(tmp3<=tmp1){
tmp1.clear();
rg int ncnt=0;
for(rg int k=j;k<len[i];k++){
if(tmp1[k]!=tmp2[k%j]) break;
ncnt++;
}
for(rg int k=j+ncnt/j*j+1;k<=len[i];k++) tmp1=tmp1+s[k];
tmp2=tmp2+'Z',tmp1=tmp1+'Z';
b[i].x=tmp2,b[i].z=tmp1;
break;
}
}
}
std::sort(b+1,b+1+n,cmp);
for(rg int i=n;i>=1;i--){
tp=0;
for(rg int j=1;j<=anstp;j++) sta[++tp]=ans[j];
for(rg int j=anstp;j>=1;j--) ans[j+1]=ans[j];
anstp++;
ans[1]=a[b[i].id][1];
for(rg int j=2;j<=len[b[i].id];j++) ad(b[i].id,j),updat(),del(j);
}
for(rg int i=1;i<=anstp;i++){
if(ans[i]==1) printf("A");
else if(ans[i]==2) printf("C");
else if(ans[i]==3) printf("G");
else printf("T");
}
printf("
");
return 0;
}
C.探寻
分析
把母矿所在处的价值看作无穷大,那么问题就转化成了最少需要多少花费才可以遍历整棵子树
对于收益减花费为正的结点,肯定能选就选
将这些节点按照花费从小到大排序
对于一个收益减花费为正,一个收益减花费为负的节点,肯定要选收益减花费为正的点
因为 (set) 中不能有重复的元素,所以对于其它的点,也要随便规定一个优先级
如果当前我们选择的节点能够直接到达
那么我们肯定要选上这个点
否则把它与它的父亲合并
设当前节点为 (now),它的父亲为 (fa)
分情况讨论
若 (cost[now]>val[fa])
此时父亲节点的收益不足以到达这个节点,所以要把合并之后节点的花费设为 (cost[fa]+cost[now]-val[fa])
同时收益也要变为 (val[now])
若 (cost[now] leq val[fa])
收益加上当前节点的贡献,变为 (val[fa]+val[now]-cost[now])
用并查集维护一下所在的集合就行了
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<set>
#define rg register
inline int read(){
rg int x=0,fh=1;
rg char ch=getchar();
while(ch<'0' || ch>'9'){
if(ch=='-') fh=-1;
ch=getchar();
}
while(ch>='0' && ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*fh;
}
const int maxn=1e6+5;
int fa[maxn],n,f[maxn];
int zhao(rg int xx){
if(xx==fa[xx]) return xx;
return fa[xx]=zhao(fa[xx]);
}
struct jie{
int id;
long long cost,val;
jie(){}
jie(rg int aa,rg long long bb,rg long long cc){
id=aa,cost=bb,val=cc;
}
friend bool operator <(const jie& A,const jie& B){
if((A.val-A.cost)>=0 && (B.val-B.cost)>=0){
return A.cost==B.cost?A.id<B.id:A.cost<B.cost;
} else {
if(A.val-A.cost==B.val-B.cost) return A.id<B.id;
return A.val-A.cost>B.val-B.cost;
}
}
};
std::set<jie> s;
long long ans,cs,val[maxn],cost[maxn];
int main(){
n=read();
rg int aa,bb,cc;
for(rg int i=2;i<=n;i++){
aa=read(),bb=read(),cc=read();
aa++;
f[i]=aa;
if(bb!=-1){
cost[i]=cc,val[i]=bb;
} else {
cost[i]=cc,val[i]=0x3f3f3f3f3f3f3f3f;
}
s.insert(jie(i,cost[i],val[i]));
}
for(rg int i=1;i<=n;i++) fa[i]=i;
rg jie tmp;
rg int now;
while(!s.empty()){
tmp=*s.begin();
s.erase(s.begin());
now=zhao(f[tmp.id]);
if(now==1){
if(tmp.cost<=cs){
cs=cs-tmp.cost+tmp.val;
} else {
ans+=tmp.cost-cs;
cs=tmp.val;
}
} else {
s.erase(s.find(jie(now,cost[now],val[now])));
if(val[now]<cost[tmp.id]){
cost[now]=cost[now]+cost[tmp.id]-val[now];
val[now]=val[tmp.id];
} else {
val[now]+=val[tmp.id]-cost[tmp.id];
}
s.insert(jie(now,cost[now],val[now]));
}
fa[zhao(tmp.id)]=zhao(now);
}
printf("%lld
",ans);
return 0;
}