比賽鏈接: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;
}