zr2019暑期高端峰会AB组day1
A. 小K与集合
这道题我在考场起码思考了两个小时,总觉得能想出什么,但其实就差一步了,我真是又傻逼又菜,连暴力都写T了,期望得分45,实际得分15,挂掉了暴力,判掉了全部元素相同和不同。
心路历程
以下是大概我前一个小时想的内容:
- 首先题目说什么什么有概率,又问必定成功的方案,其实就是要我们每一步都考虑最坏情况,我们想象对面有个人每次尽可能的阻止我达成目标,留下最废的一堆给我。
- 由于每次都最坏,所以分组不能太集中,要尽量平均,最小化损失,否则如果一堆数放一组直接被对方删掉就GG了
- 某个时刻当1的个数 ≥ k \geq k ≥k时,我就赢了,我在1放在每一组里都有就行。这个情况可以稍微推广到,比如有至少k个1必胜,由于k个2在一轮之后我只能保证一个1存活下来,所以至少有 k 2 k^2 k2个2必胜,推广到至少有 k i k^i ki个i必胜,于是我们就判掉了所有元素相同和不同(不同无解)
- (下面就想偏了)当1的个数小于k时虽然不能必胜,但是我可以前面的几组只单放一个1,然后剩下很多再在后面几组平均发放,于是就有点想直接模拟这个过程,但是万一不能平均,有余数怎么办,于是就疯狂想这个,然后我人就没了
正解
其实第三点中的本质就是k个i可以换一个i-1,于是这就像往前进位,看看能不能换来一个0,就是0位上有没有数,然后这题就没了。
然后我才突然发现我判断全部元素相同的过程(118-123行)其实就是在进位啊!!我竟然这么傻逼
考场的又臭又长菜鸡代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b)i;--)
#define mem(i,j) memset(i,j,sizeof(i))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+5;
int t,n,k,a[N],f[N],pos1[N],pos2[N],vis[N],nxt[N],last[N],g[N];
vector <int> T[N];
int task1,task2,task3;
//Task1
int s[1<<11],ans1[11],ans2[11];
vector <int> G[11];
//
//Task3
map<int,int> mp;
//
int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f*x;
}
void Judge()
{
task1=0;
if (n<=10&&k==2) task1=1;
task2=1;
FOR(i,1,n) if (a[i]!=a[1]) task2=0;
task3=1;
FOR(i,1,n)
{
if (mp[a[i]]) task3=0;
mp[a[i]]++;
}
FOR(i,1,n) mp[a[i]]=0;
return;
}
int DFS(int siz,int *A)
{
FOR(i,1,siz) if (A[i]==0) return 1;
if (siz<k) return 0;
int cnt=0;
FOR(i,1,siz) if (A[i]==1) cnt++;
if (cnt>=2)
{
if (siz==n)
{
int pos=0;
FOR(i,1,siz) if (A[i]==1) pos=i;
FOR(i,1,siz)
{
if (i!=pos) ans1[++ans1[0]]=i;
else ans2[++ans2[0]]=i;
}
}
return 1;
}
int b[2][11],tag[2];
int len=G[siz/2].size();
FOR(i,0,len-1)
{
int msk=G[siz/2][i];
int p1=0,p2=0;
tag[0]=tag[1]=0;
FOR(j,0,siz-1)
{
if ((msk>>j)&1) b[0][++p1]=A[j+1]-1;
else b[1][++p2]=A[j+1]-1;
}
tag[0]=DFS(siz/2,b[0]);
tag[1]=DFS((siz+1)/2,b[1]);
if (tag[0]&&tag[1])
{
if (siz==n)
{
FOR(j,0,siz-1)
{
if ((msk>>j)&1) ans1[++ans1[0]]=j+1;
else ans2[++ans2[0]]=j+1;
}
}
return 1;
}
}
return 0;
}
void Init()
{
FOR(i,0,(1<<10)-1)
FOR(j,0,10-1)
if ((i>>j)&1) s[i]++;
return;
}
void Task1()
{
ans1[0]=ans2[0]=0;
FOR(i,0,(1<<n)-1) G[s[i]].pb(i);
if (DFS(n,a))
{
printf("1\n");
printf("%d ",ans1[0]);
FOR(i,1,ans1[0]) printf("%d ",ans1[i]);
printf("\n");
printf("%d ",ans2[0]);
FOR(i,1,ans2[0]) printf("%d ",ans2[i]);
printf("\n");
}
else printf("0\n");
return;
}
void Task2()
{
int cnt=n,now=a[1],ok=0;
while (cnt)
{
cnt/=k;
now--;
if (now==1&&cnt>=k) ok=1;
}
if (ok)
{
int cnt1=n%k,cnt2=0;
printf("1\n");
if (cnt1) FOR(i,1,cnt1)
{
printf("%d ",n/k+1);
FOR(j,1,n/k+1) printf("%d ",++cnt2);
printf("\n");
}
FOR(i,1,k-cnt1)
{
printf("%d ",n/k);
FOR(j,1,n/k) printf("%d ",++cnt2);
printf("\n");
}
}
else printf("0\n");
return;
}
void Task3()
{
printf("0\n");
return;
}
void Solve()
{
int ok=0,tag=0,mx=0;
FOR(i,1,n) f[a[i]]++;
FOR(i,1,n) if (f[i]>=k) tag=1,mx=max(mx,i);
if (!tag)
{
printf("0\n");
return;
}
tag=0;
FOR(i,1,n) g[i]=f[i];
FOR(i,1,n/k)
{
int tmpk;
if (g[1+tag]>=k)
{
ok=1;
break;
}
else tmpk=k-g[1+tag];
FOR(j,1,mx-tag)
g[j+tag]/=tmpk;
tag++;
}
if (ok)
{
printf("1\n");
if (f[1]>=k)
{
int now1=0;
FOR(i,1,n) vis[i]=0;
FOR(i,1,n) if (a[i]==1&&now1<k-1)
{
now1++;
vis[i]=1;
printf("1 %d\n",i);
}
printf("%d ",n-k+1);
FOR(i,1,n) if (!vis[i]) printf("%d ",i);
printf("\n");
}
else
{
FOR(i,1,n) vis[i]=0;
FOR(i,1,n) if (a[i]==1) printf("1 %d\n",i),vis[i]=1;
int tmpk=k-f[1];
FOR(i,2,tmpk) nxt[i]=i-1;
nxt[1]=tmpk;
FOR(i,1,n) last[i]=1;
FOR(i,1,tmpk) T[i].clear();
FOR(i,1,n) if (!vis[i]) T[nxt[last[a[i]]]].pb(i),last[a[i]]=nxt[last[a[i]]];
FOR(i,1,tmpk)
{
int len=T[i].size();
printf("%d ",len);
FOR(j,0,len-1) printf("%d ",T[i][j]);
printf("\n");
}
}
}
else printf("0\n");
return;
}
int main()
{
// freopen("data1.in","r",stdin);
Init();
t=read();
while (t--)
{
n=read(),k=read();
FOR(i,1,n) a[i]=read();
Judge();
if (task1) Task1();
else if (task2) Task2();
else if (task3) Task3();
else Solve();
// printf("//\n");
}
return 0;
}
赛后的代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1e5+5;
int t,n,k,a[N],f[N],g[N];
vector <int> G[N],T[N];
int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f*x;
}
int main()
{
// freopen("data1.in","r",stdin);
t=read();
while (t--)
{
n=read(),k=read();
FOR(i,0,n) G[i].clear(),f[i]=g[i]=0;
FOR(i,1,k) T[i].clear();
FOR(i,1,n) a[i]=read(),G[a[i]].pb(i);
FOR(i,1,n) f[a[i]]++;
For(i,n,0) g[i]=f[i]+g[i+1]/k;
if (g[0]>=1)
{
int now=1,nowneed=1,need=1;
FOR(i,1,n)
{
int len=G[i].size();
FOR(j,0,len-1)
{
int pos=G[i][j];
T[now].pb(pos);
need--;
if ((!need)&&now<k) now++,need=nowneed;
}
nowneed*=k,need*=k;
}
printf("1\n");
FOR(i,1,k)
{
int len=T[i].size();
printf("%d ",len);
FOR(j,0,len-1) printf("%d ",T[i][j]);
printf("\n");
}
}
else printf("0\n");
}
return 0;
}
B. 小K与数据
原来并查集可以42分啊,我是傻逼吧我为什么不写并查集,又好写又有分
我写了一个
O
(
n
q
)
O(nq)
O(nq)的BFS期望得分22,但是我为什么这垃圾啊写了一个vis数组没有用上,我真是傻逼
正解
- 小性质:对于一对区间,如果它们的重叠长度大于自身的一半,那么这两段区间的并集就是一段周期串,周期为 l 2 − l 1 l_2-l_1 l2−l1
- 我们对于每一对区间右区间的点向左边连边
- 有了右区间不重叠的性质,那么每一个点只能出现在一个右区间中,那么每个点都这样向左连边形成的就是一个树结构,因为我们随机生成树的数据的时候就是这样做的
- 那么我们每次插入或者查询一个位置的字符时候,就一直往左边跳,每次跳的时候在经过的区间上标记,最终把信息留在在某棵树的树根,然后每个区间的标记怎么打呢,我们利用第一个小性质,直接记录在模周期余几时可以取到该字符
- 所以每次我们跳一次就必定跳出一个区间,区间最多n个,这样复杂度就对了, O ( n 2 ) O(n^2) O(n2)
考场代码
考场上没有写
if (!vis[...])
并查集是来赛后试了一发
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b)i;--)
#define mem(i,j) memset(i,j,sizeof(i))
using namespace std;
typedef long long ll;
const int N=1e6+5;
int n,m1,m2,q,p[N],opt[N],a,b,c,d;
int fa[N];
int task1,task2;
char ch[N][2],s[N],ans[N];
//task2
int vis[N],l,r,line[N];
//
struct seg
{
int l1,r1,l2,r2;
}f[N];
int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f*x;
}
void Judge()
{
task1=task2=0;
if (n==m1) task1=1;
if (n<=1000) task2=1;
return;
}
void Task1()
{
FOR(i,1,m1) s[p[i]]=ch[i][1];
FOR(i,1,q) ans[i]=s[opt[i]];
FOR(i,1,q) printf("%c\n",ans[i]);
return;
}
void Task2()
{
FOR(i,1,m1) s[p[i]]=ch[i][1];
FOR(i,1,q)
{
FOR(j,1,n) vis[j]=0;
r=0,l=1;
line[++r]=opt[i];
while (l<=r)
{
int tmp=line[l++];
vis[tmp]++;
if (s[tmp]!='?')
{
ans[i]=s[tmp];
break;
}
FOR(j,1,m2)
{
if (f[j].l1<=tmp&&tmp<=f[j].r1) if (!vis[tmp-f[j].l1+f[j].l2]) line[++r]=tmp-f[j].l1+f[j].l2;
if (f[j].l2<=tmp&&tmp<=f[j].r2) if (!vis[tmp-f[j].l2+f[j].l1]) line[++r]=tmp-f[j].l2+f[j].l1;
}
}
}
FOR(i,1,q) printf("%c\n",ans[i]);
return;
}
int Getfa(int x)
{
return (x==fa[x])?x:fa[x]=Getfa(fa[x]);
}
void Join(int x,int y)
{
int fx=Getfa(x),fy=Getfa(y);
if (fx!=fy) fa[fx]=fy;
return;
}
int main()
{
// freopen("data2.in","r",stdin);
n=read(),m1=read(),m2=read(),q=read();
FOR(i,1,n) fa[i]=i;
FOR(i,1,m1) p[i]=read(),scanf("%s",ch[i]+1);
FOR(i,1,m2) a=read(),b=read(),c=read(),d=read(),f[i]=(seg){a,b,c,d};
FOR(i,1,q) opt[i]=read();
FOR(i,1,m2)
FOR(j,1,f[i].r1-f[i].l1+1)
Join(f[i].l1+j-1,f[i].l2+j-1);
FOR(i,1,n) s[i]='?';
FOR(i,1,m1) s[Getfa(p[i])]=ch[i][1];
FOR(i,1,q) ans[i]=s[Getfa(opt[i])];
FOR(i,1,q) printf("%c\n",ans[i]);
return 0;
}
赛后的代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define For(i,a,b) for (register int i=(a);i>=(b);i--)
#define mem(i,j) memset(i,j,sizeof(i))
#define pb push_back
using namespace std;
typedef long long ll;
const int N=1005;
int n,m1,m2,q,a,b,c,d,pos[N],p[N],opt[N];
char ch[N][2],ans[N];
map <int,char> mp;
struct seg
{
int l1,r1,l2,r2,T;
map <int,char> mp;
}f[N];
bool cmpr2(const seg s1,const seg s2)
{
return s1.r2<s2.r2;
}
int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f*x;
}
int Which_s2(int x)
{
int s2=lower_bound(pos+1,pos+m2+1,x)-pos;
if (s2<1||s2>m2||f[s2].l2>x) return -1;
else return s2;
}
void Insert(int x,int cha)
{
int s2=Which_s2(x);
if (s2==-1)
{
mp[x]=cha;
return;
}
f[s2].mp[x%f[s2].T]=cha;
x=x-((x-f[s2].l1)/f[s2].T)*f[s2].T;
Insert(x,cha);
return;
}
char Query(int x)
{
if (mp.count(x)) return mp[x];
int s2=Which_s2(x);
if (s2==-1)
{
if (mp.count(x)) return mp[x];
else return '?';
}
if (f[s2].mp.count(x%f[s2].T)) return f[s2].mp[x%f[s2].T];
x=x-((x-f[s2].l1)/f[s2].T)*f[s2].T;
return Query(x);
}
int main()
{
// freopen("data2.in","r",stdin);
n=read(),m1=read(),m2=read(),q=read();
FOR(i,1,m1) p[i]=read(),scanf("%s",ch[i]+1);
FOR(i,1,m2) f[i].l1=read(),f[i].r1=read(),f[i].l2=read(),f[i].r2=read();
FOR(i,1,q) opt[i]=read();
sort(f+1,f+m2+1,cmpr2);
FOR(i,1,m2)
{
pos[i]=f[i].r2;
f[i].T=f[i].l2-f[i].l1;
}
// FOR(i,1,m2) printf("%d %d %d %d %d\n",f[i].l1,f[i].r1,f[i].l2,f[i].r2,f[i].T);
FOR(i,1,m1) Insert(p[i],ch[i][1]);
FOR(i,1,q) ans[i]=Query(opt[i]);
FOR(i,1,q) printf("%c\n",ans[i]);
return 0;
}
C. 小K与奇数
这题感觉暴力都不是很好写,十分的完全图就是每次每次四个四个地加点,新的四个点和原来的点构成二分图,它们之间把边跳完跳出一条路径,然后这新的四个点再按样例的方式连边
正解
- 每一个点是且仅是一条路径的端点,所以每个点的度数必定是奇数,由于所有的路径长度都是偶数,所以边数必定是偶数,这样就判掉了无解的情况
- 假设先忽略路径长度为偶数的限制,那么随机匹配 n 2 \frac{n}{2} 2n对点,连上虚边使它们的度数变成偶数,跑一个欧拉回路,虚边有 n 2 \frac{n}{2} 2n条于是刚好把这条欧拉回路断成了 n 2 \frac{n}{2} 2n符合题意条路径
- 于是现在我们考虑用长度为2的边对原图做一个覆盖,使得每一条边被覆盖且仅被覆盖一次,这样我们得到了一个新图,旧边不看(两条旧边的编号贴在新边上),在新图上跑欧拉回路就把问题变成了上面那个
- 我们仔细观察发现做完覆盖后每个点的度数奇偶性不会变,于是就
过掉了 - 还有就是怎么求这个覆盖,也就是怎么建出新图。DFS,对于每条不在DFS树上的非树边(它们必定全是返祖边,因为DFS),我们在它的上端点安排它的匹配,对于每一个点,将通向儿子的边尽可能互相匹配,这个DFS有个返回值,就是该点的父亲边有没有被匹配,这个东西在它的父节统计时有用,于是就
过掉了
赛后代码
#include<bits/stdc++.h>
#define FOR(i,a,b) for (register int i=(a);i<=(b);i++)
#define pb push_back
#define mem(i,j) memset(i,j,sizeof(i))
using namespace std;
const int N=4e5+5;
int n,m,x,y,lim,du[N],not_good=0;
int vis[N],inq[N],used[N];
int first[N],nxxt[N<<1],tote=1;
int f[N],nxt[N<<1],tot=1;
vector <vector<int> > ans;
struct E
{
int u,v,id1,id2;
}edge[N<<1],e[N<<1];
int read()
{
int x=0,f=1;
char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
return f*x;
}
void Addedge(int u,int v)
{
tote++;
nxxt[tote]=first[u];
first[u]=tote;
edge[tote]=(E){u,v,0,0};
return;
}
void Make_edge(int u,int v,int id1,int id2)
{
tot++;
nxt[tot]=f[u];
f[u]=tot;
e[tot]=(E){u,v,id1,id2};
return;
}
void Pair_edge(int &cur,int j,int u)
{
if (!cur)
{
cur=j;
return;
}
int uu,vv;
if (u==edge[cur].v) uu=edge[cur].u;
else uu=edge[cur].v;
if (u==edge[j].v) vv=edge[j].u;
else vv=edge[j].v;
Make_edge(uu,vv,cur>>1,j>>1);
Make_edge(vv,uu,j>>1,cur>>1);
// printf("%d %d %d %d %d %d\n",uu,vv,edge[cur].u,edge[cur].v,edge[j].u,edge[j].v);
cur=0;
return;
}
int DFS(int u,int pre_e)//父边匹配返回1
{
int cur=0;
vis[u]=inq[u]=1;
for (register int j=first[u];j!=-1;j=nxxt[j])
{
int v=edge[j].v;
if (!vis[v])
{
if (!DFS(v,j)) Pair_edge(cur,j,u);
}
else if (!inq[v]) Pair_edge(cur,j,u);
}
inq[u]=0;
if (!cur) return 0;//目前已经刚好匹配,父边未匹配
if (pre_e) Pair_edge(cur,pre_e,u);
return 1;
}
void Euler(int u)
{
for (register int j=f[u];j!=-1;j=nxt[j]) if (!used[j>>1])
{
used[j>>1]=1;
int v=e[j].v;
Euler(v);
if (j>lim)
{
if (ans.empty()||(!ans.back().empty())) ans.pb(vector<int>());
}
else
{
ans.back().pb(e[j].id2);
ans.back().pb(e[j].id1);
}
}
return;
}
int main()
{
mem(f,-1);
mem(first,-1);
n=read(),m=read();
FOR(i,1,m) x=read(),y=read(),Addedge(x,y),Addedge(y,x),du[x]++,du[y]++;
FOR(i,1,n) if (du[i]%2==0) not_good=1;
if ((not_good)||(m%2==1)) {printf("0\n");exit(0);}
FOR(i,1,n)
if (!vis[i]) DFS(i,0);
lim=tot;
for (int i=1;i<n;i+=2) Make_edge(i,i+1,-1,-1),Make_edge(i+1,i,-1,-1);
ans.pb(vector<int>());
FOR(i,1,n) Euler(i);
while (ans.back().empty()) ans.pop_back();
printf("1\n");
int len1=ans.size();
FOR(i,0,len1-1)
{
int len2=ans[i].size();
printf("%d ",len2);
FOR(j,0,len2-1) printf("%d ",ans[i][j]);
printf("\n");
}
return 0;
}