9[CSP-J2019] 加工零件
思路:题目要求的是1和a之间是否有长度为l的路径,如果3到1有长度为5的路径,那么3到1一定有长度为7的路径,但并不一定有长度为6的路径。所以,我们要对每个点求一遍奇数路径,和偶数路径。
#include<bits/stdc++.h>
using namespace std;
vector<int> c[1000010];
int ou[1000010];//ou[i]表示到i之间的最短偶数路径数
int ji[1000010];//ji[i]表示到i之间的最短奇数路径数
struct node{
int a,cnt;
};
void bfs(){
//初始最大值
memset(ou,0x3f,sizeof(ou));
memset(ji,0x3f,sizeof(ji));
queue<node> q;
q.push({1,0});
//如果有独立节点,要将所有独立节点标记自己到自己的路径为1
for(int i=0;i<c[1].size();i++){
ji[c[1][i]]=1;
q.push({c[1][i],1});
}
while(!q.empty()){
node k=q.front();
q.pop();
for(int i=0;i<c[k.a].size();i++){
if(k.cnt%2==1){
//如果1到当前节点的路径数为奇数,那么到下一个节点的路径数为偶数
if(k.cnt+1<ou[c[k.a][i]]){
ou[c[k.a][i]]=k.cnt+1;
q.push({c[k.a][i],k.cnt+1});
}
}else{
//如果1到当前节点的路径数为偶数,那么到下一个节点的路径数为奇数
if(k.cnt+1<ji[c[k.a][i]]){
ji[c[k.a][i]]=k.cnt+1;
q.push({c[k.a][i],k.cnt+1});
}
}
}
}
}
int main(){
//freopen("work.in","r",stdin);
//freopen("work.out","w",stdout);
int n,m,q;
cin>>n>>m>>q;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
//题目图例说明是双向图
c[u].push_back(v);
c[v].push_back(u);
}
bfs();
while(q--){
int a,l;
cin>>a>>l;
//判断l是否为奇偶数
if(l%2==0){
//如果1到a的偶数路径>l
//说明不可能存在1到a有长度为l的偶数路径
if(ou[a]>l){
cout<<"No"<<'\n';
}else{
cout<<"Yes"<<'\n';
}
}else{
//如果1到a的奇数路径>l
//说明不可能存在1到a有长度为l的奇数路径
if(ji[a]>l){
cout<<"No"<<'\n';
}else{
cout<<"Yes"<<'\n';
}
}
}
return 0;
}
10[CSP-J2019] 公交换乘
思路:用数组模拟队列存储做地铁时获得的优惠券,可以做到使用获得时间最早的优惠券,但是要记得将时间失效的券淘汰掉,否则每次遍历数组会MLE
#include<bits/stdc++.h>
using namespace std;
struct node{
long long money,times;
};
node que[100010];
int tot=0,tai=1;
//tot表示队列队尾,tai表示队列队头
bool vis[100010];
int main(){
//freopen("transfer.in","r",stdin);
//freopen("transfer.out","w",stdout);
int n;
cin>>n;
long long cnt=0;
for(int i=1;i<=n;i++){
int op,price,t;
cin>>op>>price>>t;
//坐地铁时直接cnt+=price
if(op==0){
cnt+=price;
que[++tot].times=t+45;
que[tot].money=price;
}else{
bool k=false;
while(tai<tot&&que[tai].times<t){
tai++;
} //淘汰掉时间失效的券 (wuwuwuwuwu)
for(int j=tai;j<=tot;j++){
//可以用的优惠券满足没被使用过,公交车钱不超过地铁票价,时间有效等要求
if(vis[j]!=1&&price<=que[j].money&&t<=que[j].times){
vis[j]=1;
k=true;
//使用最早获得的券,马上结束
break;
}
}
if(k==false){
cnt+=price;
}
}
}
cout<<cnt;
return 0;
}
11[CSP-J 2021] 插入排序
思路1:题目限制修改次数<=5000,所以O(nq)的时间复杂度可以过,可以在每次单点修改后前后冒泡各一次来保持有序,在维护一个有序序列,并记录原下标与先下标之间的关系(用数组记录),每次修改后更新这种关系。
#include<bits/stdc++.h>
using namespace std;
struct node{
int w,id;
};
bool cnp(node a,node b) {
if(a.w!=b.w){
return a.w<b.w;
}
return a.id<b.id;
}
node a[100010];
int t[100010];//t[i]表示在原数组a中下标为i的数在新序列中的位置(下标)
int main(){
//freopen("sort.in","r",stdin);
//freopen("sort.out","w",stdout);
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++){
cin>>a[i].w;
a[i].id=i;
}
sort(a+1,a+1+n,cnp);
//第一次统计第一次排序后的位置
for(int i=1;i<=n;i++){
t[a[i].id]=i;
}
for(int i=1;i<=q;i++){
int o;
cin>>o;
if(o==1){
int x,v;
cin>>x>>v;
//题意为修改a[x]为v,但x的下标为原数组t[x]
a[t[x]].w=v;
for(int j=n;j>=2;j--){
if(cnp(a[j],a[j-1])){
node k=a[j-1];
a[j-1]=a[j];
a[j]=k;
}
}
for(int j=2;j<=n;j++){
if(cnp(a[j],a[j-1])){
node k=a[j-1];
a[j-1]=a[j];
a[j]=k;
}
}
for(int j=1;j<=n;j++){
t[a[j].id]=j;
}
}else{
int x;
cin>>x;
cout<<t[x]<<'\n';
}
}
return 0;
}
思路2:对于每个修改操作,我们直接修改。对于每个查询操作,我们暴力查询,从头向后查找,按题意模拟即可。
#include<iostream>
#include<cstdio>
using namespace std;
int a[10011];
int main()
{
int n,q;
cin>>n>>q;
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
for(int i=1;i<=q;i++){
int xx;
scanf("%d",&xx);
if(xx==1){
int x,v;
scanf("%d%d",&x,&v);
a[x]=v;
}
else {
int x;
scanf("%d",&x);
int w=a[x];
int sum=n;//初始化x在a=[n](新数组最后一位)
for(int j=1;j<x;j++){//前1->x-1位
if(a[j]>w){
sum--;//统计比w小的(可以往前多少位)
}
}
for(int j=x+1;j<=n;j++){//后x+1->n位
if(a[j]>=w) sum--;//统计比w小的(可以往前少位)
}
printf("%d\n",sum);
}
}
return 0;
}
12[CSP-J 2021] 网络连接
思路:判断ERR的函数细节超级多,非常烦人,判断地址是否重复可以用map或set,豆肥肠刻意
#include<bits/stdc++.h>
using namespace std;
map<string,long long> mp;
bool check(string h){
int cnt1=0,cnt2=0,cnt3=0,len=h.size();
//cnt1统计'.'个数,cnt2统计':'个数,cnt3统计数字个数
long long tmp=0;
//tmp记录数字大小
for(int i=0;i<len;i++){
if((i==0||(h[i-1]=='.'||h[i-1]==':'))&&h[i]>='0'&&h[i]<='9'){
cnt3++;
}
if(h[i]=='.'||h[i]==':'){
if(h[i]=='.')cnt1++;
else cnt2++;
if(cnt1<3&&cnt2!=0||cnt3==0)return false;
//判断符合顺序是否正确,例如a:b.c.d.e或.a.b.c:df
if(tmp<0||tmp>255)return false;
//例如256.233.244.12:556
else{tmp=0;continue;}
}else if(h[i]<'0'||h[i]>'9') return false;//出现了奇怪的字符
if(i!=0&&tmp==0&&h[i-1]=='0') return false;//判断前导 0
tmp = tmp*10+h[i]-'0';
}
if(cnt1!=3||cnt2!=1||cnt3!=5)return false;//判断各个字符数量是否合法
if(tmp<0||tmp>65535)return false;//判断最后一位(e)是否合法
else return true;
}
int main(){
//freopen("network.in","r",stdin);
//freopen("network.out","w",stdout);
int n;
cin>>n;
for(int i=1;i<=n;i++){
string a,h;
cin>>a>>h;
if(!check(h)){
cout<<"ERR"<<'\n';
}else if(a=="Server"){
if(mp[h]==0){
cout<<"OK"<<'\n';
mp[h]=i;
}else cout<<"FAIL"<<'\n';
}else{
if(mp[h]==0){
cout<<"FAIL"<<'\n';
}else cout<<mp[h]<<'\n';
}
}
return 0;
}
极致压行&&美观性版
#include<bits/stdc++.h>
using namespace std;
map<string,long long> mp;
bool check(string h){
int cnt1=0,cnt2=0,cnt3=0,len=h.size();long long tmp=0;
for(int i=0;i<len;i++){
if((i==0||(h[i-1]=='.'||h[i-1]==':'))&&h[i]>='0'&&h[i]<='9')cnt3++;
if(h[i]=='.'||h[i]==':'){
if(h[i]=='.')cnt1++;
else cnt2++;
if(cnt1<3&&cnt2!=0||cnt3==0)return false;
if(tmp<0||tmp>255)return false;
else{tmp=0;continue;}
}else if(h[i]<'0'||h[i]>'9') return false;
if(i!=0&&tmp==0&&h[i-1]=='0') return false;
tmp=tmp*10+h[i]-'0';
}
if(cnt1!=3||cnt2!=1||cnt3!=5||tmp<0||tmp>65535)return false;
else return true;
}
int main(){
int n;
cin>>n;
for(int i=1;i<=n;i++){
string a,h;
cin>>a>>h;
if(!check(h))cout<<"ERR"<<'\n';
else if(a=="Server"){
if(mp[h]==0){cout<<"OK"<<'\n';mp[h]=i;}
else cout<<"FAIL"<<'\n';
}else{
if(mp[h]==0)cout<<"FAIL"<<'\n';
else cout<<mp[h]<<'\n';
}
}
return 0;
}
13[CSP-J 2021] 小熊的果篮
思路:用链表连接每一个水果,好处是删除元素很方便,统计“块”可以用vector维护每一个“块”的最左边元素。
#include<bits/stdc++.h>
using namespace std;
bool vis[1000010];
struct node{
int x,nxt,pre;
};
node a[200010];//数组模拟链表
vector<int> now;//维护每一个"块"的最左边的数
//链表删除操作
void shanchu(int x){
int p1=a[x].pre,p2=a[x].nxt;
a[p1].nxt=p2;
a[p2].pre=p1;
}
int main(){
//关闭流同步,加快代码运行时间
ios::sync_with_stdio(0);
cin.tie();
cout.tie();
int n;
cin>>n;
a[0].nxt=1;//用0做假对头,方便遍历
a[0].x=a[n+1].x=INT_MAX;//因为是假元素,所以赋值INT_MAX
for(int i=1;i<=n;i++){
cin>>a[i].x;
//
if(a[i].x!=a[i-1].x){
now.push_back(i);
}
a[i].pre=i-1;
a[i].nxt=i+1;
}
while(a[0].nxt<=n){
vector<int> tmp;//统计遍历完后的“块”的最左边数
for(int i=0;i<now.size();i++){
int xs=now[i];
cout<<xs<<" ";
shanchu(xs);
//判断x的下一个元素是否为块的最左边元素,要满足x的前一个数不等于x的后一个数(因为x已被删除),并且当前删除元素要等与下一个元素
//举例:n=12
//a[1->12]={1 1 0 0 1 1 1 0 1 1 0 0}
// 要删除第三个数,它是“块”的最左边元素,它下一个元素!=它前一个元素,所以,第四个元素是新“块”的新的最左边。
if(a[a[xs].pre].x!=a[a[xs].nxt].x&&a[xs].x==a[a[xs].nxt].x){
tmp.push_back(a[xs].nxt);
}
}
cout<<'\n';
now=tmp;
}
return 0;
}
14[CSP-J2020] 表达式(未完成)
#include<bits/stdc++.h>
using namespace std;
int a[1000010];
int son[100005][2];
int flag[1000010];
int dfs(int u,int g){
a[u]^=g;
if(u<=n){
return a[u];
}
}
void dfs2(int u){
if(u>=n)return ;
c[son[u][1]]|=c[u];
c[son[u][0]]|=c[u];
dfss(son[u][0]);
dfss(son[u][1]);
}
int main(){
//freopen("expr.in","r", stdin);
//freopen("expr.out","w",stdout);
string s;
getline(cin,s)
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
stack<int> bs;
int ck=n;
for(int i=0;i<s.size();i+=2){
//统计编号(x1,x2,x3...)
if(s[i]=='x'){
int x=0;
i++;
while(s[i]!=' '){
x=x*10+s[i]-'0';
i++;
}
i--;
b.push(x);
}else if(s[i]=='&'){
int x=b.top();
b.pop();
int y=b.top();
b.push(++ck);
a[ck]=2;
//建立字符树
son[ck][0]=x;//
son[ck][1]=y;//
}else if(s[i]=='|'){
int x=b.top();
b.pop();
int y=b.top();
b.push(++ck);
a[ck]=3;
son[ck][0]=x;
son[ck][0]=y;
}else{
flag[b.top()]^=1;
}
}
int ans=dfs(ck,flag[ck]);
dfs2(ck);
int q;
cin>>q;
for(int i=1;i<=q;i++){
int t;
cin>>t;
if(c[t]){
cout<<ans<<'\n';
}else{
cout<<!ans<<'\n';
}
}
return 0;
}