第一次爆零
我真是大起大落(详见)
逻辑啊,逻辑啊
信息学就是数学+英语+逻辑学
T1 IPv6
超级日隆
细节,条件限制很多
如下:
- 两个:之间最多有4个字符
- 最多有两个::省略
- :个数不多于7个
- 如果有7个:,且存在连续的,则其位置一定在最前或最后
- 16进制!虽然数据都合法
因此,用到方法:
int p1=0/验算两个:之间有没有超过4个字符/;
int p2=0/储存:的个数/;
int p3=0/储存连续两个:的个数/;
int p4=0/如果存在不合法,p4记录/;
int k=0/指针/;
int point=0/储存两个连续:中后一个的位置/;
我对zgs的代码的批注(有理有据地让你们知道什么叫丑)
#include<bits/stdc++.h>
using namespace std;
char s[100],a[100];//s存简化码
int n;
int main()
{
// freopen("ipv6.in","r",stdin);
// freopen("ipv6.out","w",stdout);
scanf("%d\n",&n);
for(int ii=1;ii<=n;ii++)
{
memset(s,'0',sizeof(s));
memset(a,'0',sizeof(a));
char c;
int p1=0/*验算两个:之间有没有超过4个字符*/;
int p2=0/*储存:的个数*/;
int p3=0/*储存连续两个:的个数*/;
int p4=0/*如果存在不合法,p4记录*/;
int k=0/*指针*/;
int point=0/*储存两个连续:中后一个的位置*/;
while((c=getchar())!=EOF && c!='\n')
{
s[++k]=c;//读入一个处理一个
if(c==':') {p1=0; p2++;}
else p1++;//记录:的个数
if(p1>4 || p2>7) p4=1;//:之间超4个,舍;:个数多于7个,舍;
if(c==':' && s[k-1]==':') {p3++;point=k;}//记录连续的:组数与后一个:的坐标
if(p3>1) p4=1;//超过一组,舍;
}
if(!p3 && p2<7) p4=1;//没有省略但是:个数少了,舍
if(p3 && p2==7 && point!=2 && point!=k) p4=1;
//连续:的位置不对(如果有7个:,且存在连续的,则其位置一定在最前或最后)
for(int i=1;i<=k;i++) a[i]=s[k-i+1]; //倒序存入a ,目的是处理前导0
if(p4) {printf("INVALID\n");continue;} //p4记录了不合法的情况,若不合法,舍
int q=0;//q是s的指针,注意,这里的s不是原来的s
p1=0;
for(int i=1;i<=k;i++)
{
s[++q]=a[i];
p1++;
if(a[i]==':')
{
q--;
while(p1<5)
{
s[++q]='0';
p1++;//凑够4位
}
s[++q]=':';
p1=0;//四位处理完,打:结尾
if(a[i+1]==':')//省略全0的情况
{
while(p2<=7)//补齐省略的全0
{
for(int j=1;j<=4;j++)
s[++q]='0';
s[++q]=':';
p2++;
}
q--;//基于i发现i+1与i是省略的:,要退格
p1=4;
}
}
}
for(int i=39;i>=1;i--) printf("%c",s[i]);//倒序输出答案
cout<<endl;
}
return 0;
}
T2 Starwar
这不就是暑假做过的那道题吗??!T_T
并查集,倒着加点(一会儿把梁老讲课的内容消化一下,今天zgs让他给我们讲了带权并查集,我似乎又回到了暑假的听天书状态T_T)
模块化让你的代码更清晰
找到连通块的祖先:
枚举边优化:i+=2
加点增减连通块的数量:不需要再求一遍,如果有合并,连通块数量-1(注意,添加一个点之后连通块数量自然要+1)
注意这道日隆题星球的编号从0开始,+1变成习惯的
星期六还T3个,现在T2个
最后发现zgs的网站里面printf比cout慢。。。
damn it浪费我时间
这个代码是ok的↓↓↓↓↓↓
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define re register
const int NNN=(int)1e5+10;
int n,m,k;
int tot,first[NNN];
struct bian{
int ori,aim,nxt;
}edge[NNN<<2];
int a[NNN>>1];//摧毁顺序
int father[NNN];
bool exist[NNN];//存在的星球
int ans,ansout[NNN>>1];
inline int in{
int i=0,f=1;char ch;
while((ch>'9'||ch<'0')&&ch!='-')ch=getchar();
if(ch=='-')ch=getchar(),f=-1;
while(ch>='0'&&ch<='9')i=(i<<1)+(i<<3)+ch-48,ch=getchar();
return i*f;
}
inline void Init(){
for(re int i=1;i<=n;i++)father[i]=i,exist[i]=true;
ans=0;
}
inline void Add(int u,int v){
tot++;
edge[tot].nxt=first[u];
first[u]=tot;
edge[tot].aim=v;
edge[tot].ori=u;
}
inline void GetData(){
n=in,m=in;
Init();
for(re int i=1;i<=m;i++){
int u=in+1,v=in+1;
Add(u,v),Add(v,u);
}
k=in;
for(re int i=1;i<=k;i++)a[i]=in+1,exist[a[i]]=false;
}
inline int getfather(int i){
if(father[i]==i)return father[i];
father[i]=getfather(father[i]);
return father[i];
}
inline void Solve(){
for(re int i=1;i<=tot;i+=2){
int u=edge[i].aim,v=edge[i].ori;
if(exist[u]&&exist[v]){
u=getfather(u),v=getfather(v);
father[u]=v;
}
}
for(re int i=1;i<=n;i++)
if(exist[i]&&father[i]==i)
ans++;
for(re int i=k;i;i--){
ansout[i]=ans;
exist[a[i]]=true;
ans++;
for(re int e=first[a[i]];e;e=edge[e].nxt){
int u=getfather(edge[e].ori),v=edge[e].aim;
if(exist[v])
if(u!=getfather(v))
father[u]=getfather(v),ans--;
}
}
ansout[0]=ans;
for(re int i=0;i<=k;i++)printf("%d\n",ansout[i]);
}
int main(){
GetData();
Solve();
return 0;
}
T3 tree
转化思想
先整一个最小生成树,在通过一点操作得到次小生成树
MST(minimum spanning tree)
经典的Kruscal,并查集实现(还可以用Prim,可惜我不会)
运用到贪心思想
先讲一下次短路:(当然NOI选手还可以炫一炫A*)
最短路上的边依次删去,再求最短路
得到的路径中最小的就是次短路长度
再提醒一下:求路径上的点的方法——用栈(已知起点和终点)
类似于次短路
得到MST之后,从不在MST里的边中选一条,称为u
那么这条边一定会与MST构成一个环
于是删去该环上最大且不为该边的边,称为v
于是,
依次枚举不在MST里的边,得到
那么次小生成树的权值总和就
我水平有限,复制一个标程在下面
但是他用的是树剖在做
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL INF=(LL)1e12;
const int N=(int)1e5 + 20;
const int M=3*(int)1e5 + 20;
int n,m,size,father[N],first[N];
int d[N],g[N][20],maxx[N][20][2];
int mx[2],p[N*3];
bool b[M],v[M];
LL ans,mst;
struct node
{
int a;
int b;
int c;
}e[M];
struct node1
{
int to;
int len;
int next;
}bian[M*2];
void inser(int x,int y,int w)
{
size++;
bian[size].next=first[x];
first[x]=size;
bian[size].to=y;
bian[size].len=w;
}
void init()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
}
bool cmp(const node &a,const node &b)
{
return a.c<b.c;
}
int search(int v)
{
if (father[v]==v)
return v;
father[v]=search(father[v]);
return father[v];
}
void MST() //krusakal算法求最小生成树
{
for (int i=1;i<=n;i++)
father[i]=i;
int t=0;
sort(e+1,e+m+1,cmp); //按边的长度排序
for (int i=1;i<=m;i++) //从小到大枚举每条边
{
int x=search(e[i].a);
int y=search(e[i].b);
if (x!=y) //第i条边两端不在一个集合
{
father[x]=y; //合并
mst+=e[i].c; //最小生成树的权值
t++;
b[i]=true; //标记i边已经放进最小生成树
inser(e[i].a,e[i].b,e[i].c); //把i边建立邻接表
inser(e[i].b,e[i].a,e[i].c); //反向建立邻接表
}
if (t==n-1) break; //如果加进了n-1条边,则最小生成树加边完成
}
}
void up_max(int &a,int &b,int w) //更新
{
if (w>a)
{b=a;a=w;return;}
if (w>b&&w!=a)
{b=w;return;}
}
void bfs()
{
memset(g,-1,sizeof(g));
int head=0,tail=1;
d[1]=1; //d是深度
p[1]=1; //点1入队列
v[1]=true;
while (head<tail)
{
int x=p[++head];
for (int u=first[x];u;u=bian[u].next) //枚举x出发的每条边
if (!v[bian[u].to]) //边指向的点不在队列中
{
int y=bian[u].to;
d[y]=d[x]+1;
v[y]=true;
g[y][0]=x;
maxx[y][0][1]=bian[u].len;
int k=0;
while (g[y][k]!=-1) //倍增 maxx[y][k] 表示这段路径上的最长边和次长边
{
g[y][k+1]=g[g[y][k]][k];
up_max(maxx[y][k+1][1],maxx[y][k+1][0],maxx[y][k][0]);
up_max(maxx[y][k+1][1],maxx[y][k+1][0],maxx[y][k][1]);
up_max(maxx[y][k+1][1],maxx[y][k+1][0],maxx[g[y][k]][k][1]);
up_max(maxx[y][k+1][1],maxx[y][k+1][0],maxx[g[y][k]][k][0]);
k++;
}
p[++tail]=y;
}
}
}
int jump(int x,int l)
{
for (int i=19;i>=0;i--)
if ((1<<i)<=l)
{
l-=1<<i;
up_max(mx[1],mx[0],maxx[x][i][0]);
up_max(mx[1],mx[0],maxx[x][i][1]);
x=g[x][i];
}
return x;
}
void find(int v) //最小生成树和次小生成树之间只有一边之差,枚举去掉哪条边,新连接一条边会构成一个环,环上找最值
{
int x=e[v].a,y=e[v].b,w=e[v].c;
mx[0]=mx[1]=0;
if (d[x]<d[y])
swap(x,y);
x=jump(x,d[x]-d[y]);
if (x==y)
{
if (w!=mx[1])ans=min(ans,mst+w-mx[1]);
else ans=min(ans,mst+w-mx[0]);
}
for (int i=19;i>=0;i--)
if (g[x][i]!=g[y][i])
{
up_max(mx[1],mx[0],maxx[x][i][0]);
up_max(mx[1],mx[0],maxx[x][i][1]);
up_max(mx[1],mx[0],maxx[y][i][0]);
up_max(mx[1],mx[0],maxx[y][i][1]);
x=g[x][i];
y=g[y][i];
}
up_max(mx[1],mx[0],maxx[x][0][1]);
up_max(mx[1],mx[0],maxx[y][0][1]);
if (w!=mx[1])ans=min(ans,mst+w-mx[1]);
else ans=min(ans,mst+w-mx[0]);
}
int main()
{
// freopen("tree.in","r",stdin);
// freopen("tree.out","w",stdout);
ans=INF;
init();
MST();
bfs();
for (int i=1;i<=m;i++)
if (!b[i]) find(i);
cout<<ans<<endl;
return 0;
}