[游记]2022高考集训1-2022.6.5
好像是 ZhengRuiOI 2021 CSP 7 连测 day2 ?
A层的大佬们进来比赛了有点慌
肯定前十全是A层Dalao罢
考的又是一堆字符串鬼题
对于我这个不会字符串的蒟蒻抱有极大的恶意
所以不出所料炸了
A. 2A
B. 2B
C. 2C
D. 2D
这名字真整(
赛时得分:300/400
赛时排行:Rank5
Update:经过LyinMX大佬和artalter大佬的不懈努力,增加了两组Hack
排名有了一点小小的调整
原排名:
真实排名:
虽然排名好像没变
%%%Eafoo太强了AK了,%%%CDsidi大佬,%%%Chen_(一点都不)jr大佬,%%%Lyin大佬把我吊着打
(话说这几位大佬都是A层的……太强了%%%)
赛时先看T1
A. 2A
先吐槽一下题目与内容严重不符,RNM退钱
言归正传,这种暴力模拟恶心就在于难于判重
- 注意连续出现好多个点12.....12..12.12
- 注意有前导0
- 注意000.000.000.000这种奇葩情况
- ……
大概也就这点了,大佬们考虑加一些Hack数据
先加着,如果卡掉了我我再改
Update:好像真的卡掉了改一下改一下
Hack: 100000000000000000000.0.0.0 (1e20)
丧心病狂的卡 long long
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<string> #include<iostream> #define WR WinterRain #define int long long #define IP signed using namespace std; const int WR=100; char str[WR]; int len,ptlen; int ad[WR],cnt; bool flag,pre,tag; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } IP main(){ freopen("ip.in","r",stdin); freopen("ip.out","w",stdout); cin>>str; len=strlen(str); flag=true,pre=false,tag=false; for(int i=0;i<len;i++){ if(str[i]>='0'&&str[i]<='9'){ if(!pre){ pre=true; cnt++; if(str[i]=='0') tag=true; } ptlen++; ad[cnt]=ad[cnt]*10+str[i]-48; if(ad[cnt]>255){ ad[cnt]=255,flag=false; }//这里,善良的artalter卡爆了long long }else{ if((ad[cnt]>255)||//判断是否越界 (tag&&ad[cnt]!=0)||//判断是否出现前导0 (ptlen>1&&!ad[cnt])||//判断000.000.000.000这个鬼 (str[i]!='.')||//判断间隔符是不是'.' ((str[i-1]>='0'&&str[i-1]<='9')^(str[i+1]>='0'&&str[i+1]<='9'))){ flag=false;//还要判分隔符两边是不是数字 } pre=false,tag=false,ptlen=0; } } if(cnt!=4||(tag&&ad[cnt]!=0)) flag=false; if(flag){ printf("YES"); fclose(stdin); fclose(stdout); return 0; }else printf("NO\n"); for(int i=1;i<=4;i++){ if(ad[i]>255) ad[i]=255; } for(int i=1;i<=3;i++){ printf("%d.",ad[i]); } printf("%d",ad[4]); fclose(stdin); fclose(stdout); return 0; }
但总归是十分简单的
然后开T2:怎么又是字符串?
B. 2B
额这个名字……
……
显然的我们可以发现A其实是没有用的,有用的是P
自然我们想让被弹掉的A尽可能多
于是我们可以从前向后扫一遍,发现AP就弹掉
剩下的PP再弹
(这不比T1简单
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<string> #include<iostream> #define WR WinterRain #define int long long #define APstr signed using namespace std; const int WR=1001000; char str[WR]; int acnt,pcnt; int len; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } APstr main(){ freopen("apstr.in","r",stdin); freopen("apstr.out","w",stdout); cin>>str; len=strlen(str); for(int i=0;i<len;i++){ if(str[i]=='A') acnt++; else if(acnt>0) acnt--; else pcnt++; } printf("%lld",acnt+(pcnt%2)); fclose(stdin); fclose(stdout); return 0; }
很快切了看T3
C. 2C
额……怎么还是字符串???
首先考虑是否可以用map映射不然会出人命的
然后考虑菱形的概念是什么?
数据里有一个 1决定2,1和2决定3,1、2和3共同决定4……
这不属于菱形:具体原因见第3条,类X不是从Y派生的,类Y也不是从类X派生的
意思是一直追溯祖先如果没有别的分叉汇合到一点才叫菱形 ……
然后就没有什么大问题了,写就可以愉快的AC
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<string> #include<map> #include<bitset> #include<iostream> #define WR WinterRain #define int long long #define Class signed using namespace std; const int WR=1010; int n,fa[WR]; int fth,tot; map<string,int>mp; bitset<WR>anc[WR],boom[WR]; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } Class main(){ freopen("class.in","r",stdin); freopen("class.out","w",stdout); n=read(); for(int i=1;i<=n;i++){ bool wrong=false; fth=0; string nme,rlt,bin; cin>>nme;cin>>bin;cin>>rlt;//有个冒号恶心人 while(rlt[0]!=';'){//输入祖先 if(!mp[rlt]) wrong=true;//如果并没有这个祖先标记 fa[++fth]=mp[rlt];//记录一下父亲们 //你有几个爹啊————lcr cin>>rlt;//不能加break否则会重复输入 } bitset<WR>tmp;//tmp的第i位表示i号节点是不是目前节点的父亲 for(int j=1;j<=fth;j++){ tmp[fa[j]]=1; } for(int j=1;j<=fth;j++){//如果爆炸了打标记 if((tmp&boom[fa[j]]).any()) wrong=true;//如果不幸和父亲的雷点重合就炸 }//这个boom在下面有讲 if(wrong||mp[nme]){//之前声明过也要判 printf("greska\n"); continue; } printf("ok\n"); mp[nme]=++tot; anc[tot][tot]=1;//加入新的映射 for(int j=1;j<=fth;j++){ anc[tot]|=anc[fa[j]];//anc表示ancestors,维护祖先 //和上面的tmp差不多 } for(int j=1;j<tot;j++){ if(!anc[tot][j]&&(anc[j]&anc[tot]).any()){ boom[tot][j]=1; //这里差不多是精髓了 //如果j并非当前节点的祖先,而且当前节点和j节点共用一些祖先 //那么可能形成菱形 //这里我们要做标记,就像上面去使用 } } } fclose(stdin); fclose(stdout); return 0; }
还剩两个小时看T4
当然是字符串……才怪
D. 2D
一看就非常不可做
得出结论:不会(逃ε=ε=ε=┏(゜ロ゜;)┛
所以两个小时就这么过去了
写了些化学被谷老师diss
然后Eafoo居然AK,发现的确可以暴力
只能打出暴力,但因为数据太水暴力拓扑序能满分……
Update:Lyin加了两组Hack,过不去了(悲
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<cstdio> #include<cstring> #include<string> #include<queue> #define WR WinterRain #define int long long #define KdGraph signed using namespace std; const int WR=1000100; struct Edge{ int pre,to; }edge[WR<<1]; int n,m,M,N,B; int sm,sn,sb; int head[WR],cnt; int deg[WR]; bool del[WR],vis[WR]; int in[WR]; int ans1,ans2=-1e18; int tot,maxx; int read(){ int s=0,w=1; char ch=getchar(); while(ch>'9'||ch<'0'){ if(ch=='-') w=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ s=(s<<3)+(s<<1)+ch-48; ch=getchar(); } return s*w; } void add(int u,int v){ edge[++cnt].pre=head[u]; edge[cnt].to=v; head[u]=cnt; } void dfs(int u){ sb+=in[u]; sn++; vis[u]=1; for(int i=head[u];i;i=edge[i].pre){ int v=edge[i].to; if(del[v]) continue; sm++; if(!vis[v]) dfs(v); } } void delet(int k){ queue<int>q; for(int i=1;i<=n;i++){ if(!del[i]&°[i]==k){ del[i]=1; q.push(i); tot--; } } while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];i;i=edge[i].pre){ int v=edge[i].to; if(!del[v]){ deg[v]--; if(deg[v]==k){ del[v]=1; tot--; q.push(v); } } } } } KdGraph main(){ freopen("kdgraph.in","r",stdin); freopen("kdgraph.out","w",stdout); n=tot=read(),m=read(); M=read(),N=read(),B=read(); for(int i=1;i<=m;i++){ int u=read(),v=read(); add(u,v);add(v,u); deg[u]++,deg[v]++; } for(int i=1;i<=n;i++){ in[i]=deg[i]; if(!in[i]){ tot--; del[i]=1; } maxx=max(deg[i],maxx); } for(int k=1;k<=maxx&&tot;k++){ memset(vis,false,sizeof(vis)); for(int i=1;i<=n;i++){ if(!del[i]&&!vis[i]){ sn=sm=sb=0; dfs(i); sb-=sm; sm>>=1; int res=sm*M-sn*N+sb*B; if(res>=ans2){ ans1=k; ans2=res; } } } delet(k); } printf("%lld %lld\n",ans1,ans2); return 0; }
首先可以发现,如果不考虑连通性问题,(k + 1)-degree 一定是 k-degree 的子图
如果一个点属于 (k + 1)-degree 而不属于 k-degree,我们可以把这个点标记为 k + 1
所以我们可以考虑找到 k 最大的 k-degree,然后考虑不断的拓展这个子图,去得到 k 比较小 的 k-degree
显然每一次加入一批相同标记的点,可以拓展得到 k 减小的 k-degree
假设我们已经知道了当前图的 score ,我们考虑新增点/边会带来的 score 变化
我们首先给所有的节点一个 rank
• rank(v) > rank(u) 当且仅当
– v 的标记 > u 的标记
– v 的标记 = u 的标记且 v > u
• 这个部分可以用 bin-sort 完成
我们可以根据边两边点的 rank 对边分类,用 E(v, >) 表示 v 的边中另一个端点比 v rank 大 的边,其余的表示以此类推
于是,考虑新增一批点(标记相同的点):
对于每一个新加入的点 u:
• ∆n = 1
• ∆m = |E(u, >)| + 1 2 |E(u, =)|
• ∆b = |E(u, <)| − |E(u, >)|
于是从标记大的点到小的点依次加入计算 score,得到最大 score 的 k 即可
因为不断加入点,可能使得本来不连通的两个子图联通起来,这部分可以用并查集维护
时间复杂度 O(m + n)
题解是这么说的
于是我也就这么看了
正在研读,等读懂了再补(虽然大概率补不了了
Update:补一版smtwy的
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//2D //求最大子图的tarjan的强连通思想 //魔法少女运用并查集维护相对关系的思想 //再有点带权并查集银河英雄传说的思想 #include <cstdio> #include <cstring> #include <iostream> #include <cmath> #include <queue> #include <vector> #include <algorithm> using namespace std; typedef long long ll; const int mx=1000000+1000; int n,m; ll M,N,B; int len; int du[mx];//记录节点度数 int head[mx*5]; int fa[mx]; ll ans=-999999999999999999,an1; int q[mx]; int l,r; struct Nodee{ int belong;//这个点属于哪个k-degree图,belong存的是k值 它属于vector体系 int ns;//这个点所属联通块的点数 int bs;//边界边数 int ms;//边数 //因为我们用并查集维护关系,所以除了father的值,其它的值都只是中转,并不是严格定义上的 “这个点所属联通块的点数” }o[mx];//下标是点的编号 struct Node{ int from; int to; int next; }e[mx*5];//建图 vector <int> num[mx];//储存k-degree的vector void Insert(int u,int v){ e[++len].from=u; e[len].to=v; e[len].next=head[u]; head[u]=len; du[u]++; } //前向星建图并记录度数 inline ll read(){ ll x=0; ll w=1; char ch=getchar(); while(isdigit(ch)==false){ if(ch=='-')w=-1; ch=getchar(); } while(isdigit(ch)==true){ x =(x<<3)+(x<<1)+(ch^48); ch=getchar(); } return x*w; } int find(int rt){ if(fa[rt]==rt)return rt; return fa[rt]=find(fa[rt]); } void merge(int x,int y){ int xx=find(x); int yy=find(y); if(xx!=yy){//如果本次相连的两个点不属于同一个祖先 fa[yy]=xx; o[xx].ns+=o[yy].ns;//两个联通块的节点个数直接相加 o[xx].ms+=o[yy].ms+1;//边数相加并加1,加1是因为在并查集中,它们原本不相连,是我们此刻给它连了一条边 //而它们之所以可以相连,是因为它们在原始图中是相连的,这条边一直存在 o[xx].bs+=o[yy].bs-2;//在并查集的定义中,此刻相连的这条边,原本在不相连时是两个连通块的边界边,它一连不是边界边,所以要减2 } else {//如果属于同一个祖先 o[xx].bs-=2;//那么两个点之间这条边,原本是双方各自的边界边,此刻成了相连边,所以减2 o[xx].ms++;//加的就是之间这条边 ,和上一行那个是同一条边 } //并查集这里为什么这么加一定要理解,这样我们就把题干要求的n,m,b通过带权并查集求出 } void Solve(){ n=read();m=read(); M=read();N=read();B=read(); //如果不写快读会被lyin卡掉,来自“挚友”的hack l=1; r=0; for(int i=1;i<=m;++i){ int x,y; x=read(); y=read(); Insert(x,y); Insert(y,x); } for(int i=1;i<=n;++i){ fa[i]=i; o[i].ns=1;//初始化每个点都是一个独立连通块 o[i].bs=du[i]; //刚开始,对于一个“独立”的点而言,它的度数就是边界边数(所有相连的边都是边界边) //这里提醒一下,并查集记录的是联通块间的相对关系,vector存的是k-degree子图,e前向星是存储整张原始图,三者相互独立 } int cnt=0; int maa=0; for(int i=0;i<n;++i){//循环枚举k-degree的k值 for(int j=1;j<=n;++j){ if(du[j]==i){ if(i==0){//这个零度点的地方我处理的不太好,应该还可以再妙 cnt++; } else { q[++r]=j;//如果度值等于当前k值,扔进队列 } } } if(i==0)continue; while(l<=r){ int u=q[l]; l++;//这里如果用queue会被lyin卡掉 num[i].push_back(u);//把相同度数的点放进一个ve o[u].belong=i;//同时记录属于关系,这样后面操作更方便一些 //这两步跟强连通的存储差不多 cnt++; for(int k=head[u];k;k=e[k].next){ int v=e[k].to; du[v]--;//v点有一个相连的点u在k=i的范围内,u已经在i被归属,不会再对v点在更大的k-degree做贡献 //所以直接减去 if(du[v]==i)q[++r]=v;//这里就是开队列的妙处,如果v减去之后度值也属于i了,就压进去,存起来 } } l=1,r=0; if(cnt==n){//假设有七个点,度的可能性为0到6,但是可能度为2的有3个,度为3的有4个 //我们直接找全了7个点,就直接break就行,没有必要再循环下去 //当然据lyin说当数据大了之后它总会跑到很大,后面多几次空循环也无所谓,所以其实没有什么用 maa=i;//记录所有的点,度数最大为几 break; } } for(int i=maa;i>0;--i){//从k值最大的k-degree图开始,从里往外扩展 int nu=num[i].size(); for(int j=0;j<nu;++j){//枚举这个k-degree图中的所有点 for(int k=head[num[i][j]];k;k=e[k].next){ int v=e[k].to; if(i<o[v].belong){//如果当前i(k)值小于v的点k值 //说明当前是小的外层,可以与内层相连 //比如如果是红点k=3,那么这里一直不会执行,因为它的k是最大的,所以只会执行下面那个 //把红点相互连起来。如果是绿点k=2,看最左边那个绿点,它可以连一个黄点k=1,没有任何用,跳过 //它还可以连一个红点,k=3,那么就会执行这一步,扩展出去 //这是用绿点向里连接,等于是又倒了一下。其实直接用红点向外连也行,而且更好理解 //但是当时我考试就这么麻烦的写得 //结果他们改题就都这么写hhhhhhhc,这才是最为高深的防伪标记 merge(num[i][j],v); continue; } if(i==o[v].belong && num[i][j]<v){//如果k值一样也要相连 //int u=num[i][j]. u<v,因为这是无向图,所以u,v可以互相找到,但是边只有一条 //如果你重复合并值肯定就错了,所以这就保证了只会连一次,写成v<u也行 //其实就相当于一个bool vis,但是这个题一个点可能会被连接多次,写vis不好处理 //而且这么写岂不是更帅,并起到一个强者检测器(这个词是给351238讲的时候想到的)的功能 //因为这实际上就是一个图论找边不用vis的小优化,真正强的一眼就能看透 //但是看不懂或者不愿意思考的肯定就很难了(这是351238说的,我没有别的意思hhhhhhc) merge(num[i][j],v); } } } for(int j=0;j<nu;++j){ //这里最后把这个k-degree子图的所有点再都循环一遍,找father,求解 //一定要都循环一遍,因为实际数据可能会有多个联通块(它有的数据甚至有两万多个零度点) //不同联通块可能都有k值为2的点,它们在并查集肯定无关联,但是在ve中都存到一起了 //就比如图的两块红区域,就要分开都算一遍,因为没有绿点加进来的时候它们肯定不连通 int fat=find(num[i][j]); ll op=M*o[fat].ms-N*o[fat].ns+B*o[fat].bs; if(ans<op){ ans=op; an1=i; } } } printf("%d %lld\n",an1,ans);//ok,开始调试环节 } int main(){ freopen("kdgraph.in","r",stdin); freopen("kdgraph.out","w",stdout); Solve(); return 0; }
本文来自博客园,作者:冬天丶的雨,转载请注明原文链接:https://www.cnblogs.com/WintersRain/p/16343862.html
为了一切不改变的理想,为了改变不理想的一切