比赛链接:http://noi.ac/contest/232
A. 题面很迷,相当于要通过一些环的移动,使得最终每个盒子里面都有想要的球。然后我们会发现只要每个盒子里都有自己想要的球并且恰好用完了所有的球的话,一定形成一些环(其实可以考虑循环节之类的反正我觉得挺好想的。。。)那就直接跑二分图咯,然后能得到为N的匹配就是对的,不然就GG
#include <bits/stdc++.h>
using namespace std;
//n个盒子, n个球,m个关系,问是否能做到每个盒子里都有想要的球
//先找到这样的排列->发现这样的过程一定存在
//若能满足,那么一定存在若干环可以搞
//所以直接二分图找最大排列是否为n即可
const int maxn=10005;
int n,m;
int ma[maxn],e[maxn][maxn],vis[maxn];
int find(int x)
{
for(int i=1;i<=n;i++)
{
if(e[x][i]&&!vis[i])
{
vis[i]=1;//在此次不行或者有了匹配就别动他了
if(!ma[i]||find(ma[i]))
{
ma[i]=x;
return 1;
}
}
}
return 0;
}
void work()
{
memset(e,0,sizeof(e));
memset(ma,0,sizeof(ma));
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d%d",&x,&y);
e[x][y]=1;
}
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
ans+=find(i);
}
if(ans==n) printf("YES\n");
else printf("NO\n");
}
int main()
{
while(cin>>n>>m)
{
work();
}
return 0;
}
B. 有趣的字符串,感觉算法复杂度并不是很难(虽然我依然不会),但思路很巧妙。每个字符串进行循环一定能找到字典序最小的至少1个(没问题吧)那若是2个串经过循环可以变得一样,是不是就等价于2个字符串字典序最小的串相同呢?
于是考虑最小表示法则23333
具体算法我也是才学的。大概就是2个指针i,j,不断更新那个比较菜的最终能保证较小的那个是代表了最小表示法则的起点。
/*
循环同构
判断 kmp
最小表示法
*/
#include <bits/stdc++.h>
using namespace std;
int n;string s1,s2;
int solve(string s)
{
int i=0,j=1,k=0;
while(i<n&&j<n&&k<n)
{
int t=s[(i+k)%n]-s[(j+k)%n];
if(t)//t!=0
{
if(t>0) i+=k+1;
else j+=k+1;
k=0;
}//说明ij在k范围内都是一样的呢
else k++;
if(i==j) j++;//保证j在i的后面
}
return min(i,j); //出来的时候可能j会在上一步++,i才可能是最小的
}
//寻找一个子串的最小表示法则
int main()
{
int T;
scanf("%d%d",&n,&T);
cin>>s1>>s2;
int p=solve(s1),q=solve(s2);
//找到2个串的最小表示法则
for(int i=0;i<n;i++)
{
if(s1[(i+p)%n]!=s2[(i+q)%n]) {printf("NIE");return 0;}
}
printf("TAK\n");
if(T) for(int i=0;i<n;i++) cout<<s1[(i+p)%n];
return 0;
}
C. 又是一道有趣的图论(事实上加一个简答预处理可以得到70分的高分,鼓掌)
神仙正解是分治+最短路(dij)orz感觉是普及组难度hhh但全场没有人A
离线操作,把所有点按照去掉的点分类搞到一个vector里面,进行分治。l-r表示l-r之间的点不用的最短路。每次用l-mid更新最短路,接着就继续分治mid+1-r(因为此时只有这个区间没有更新)然后到了l==r的时候,就可以更新答案了。
(好聪明啊啊啊我好蠢啊)
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int Inf=100000000;
int n,m,Q;int s[210][210],ans[100010];
struct node
{
int t,x,y;
};
vector<node> q[210];
void solve(int l,int r)
{
if(l==r)
{
for(int i=0;i<q[l].size();i++)
{
node tmp=q[l][i];
ans[tmp.t]=(s[tmp.x][tmp.y]>Inf?-1:s[tmp.x][tmp.y]);
//第几个询问的结果搞出来了,因为除了l,别的都已经用来更新答案了
}
return;
}
int mid=(l+r)>>1,f[210][210];//函数里的数组不能开的太大
memcpy(f,s,sizeof(s));
for(int i=l;i<=mid;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
s[j][k]=min(s[j][k],s[j][i]+s[i][k]);
solve(mid+1,r);//mid+1-r之间的点都不能用所以更新1-mid
memcpy(s,f,sizeof(s));
for(int i=mid+1;i<=r;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
s[j][k]=min(s[j][k],s[j][i]+s[i][k]);
solve(l,mid);
//同理
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
memset(s,127/3,sizeof(s));
for(int i=1;i<=m;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
s[x][y]=s[y][x]=min(s[x][y],z);
}
for(int i=1;i<=Q;i++)
{
int x,y,z;scanf("%d%d%d",&x,&y,&z);
q[x].push_back((node){i,y,z});
}
//把所有的询问按x分类
solve(1,n);
for(int i=1;i<=Q;i++) printf("%d\n",ans[i]) ;
return 0;
}