2020 Multi-University Training Contest 3
1004Tokitsukaze and Multiple
http://acm.hdu.edu.cn/showproblem.php?pid=6794
题意:给一个包含n个数的线段,问最多可以分成多少段使得每一段的和都是p的倍数,输出段数
思路:用map维护前缀和sum%p ,如果ma[sum%p]>0则说明这一段存在一个和是p的倍数,继续下一段的计算,此时字典要清0,且mp[0]=1;
code:
#include<iostream>
#include<cstdio>
#include<map>
using namespace std;
typedef long long ll;
int n,p;
int main()
{
std::ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--){
map<int,int> ma;
cin>>n>>p;
int x;
ll sum=0;
int ans=0;
ma[0]=1;
for(int i=1;i<=n;i++){
cin>>x;
x%=p;
sum=(sum+x)%p;
if(ma.count(sum)){
ans++;
ma.clear();
ma[0]=1;
sum=0;//前缀和一定要从头开始算
}
else ma[sum]++;
}
cout<<ans<<endl;
}
return 0;
}
Parentheses Matching
http://acm.hdu.edu.cn/showproblem.php?pid=6799
思路:我们要使括号匹配并且使字典序最小,就必须从左到右遍历,找到与")"匹配的左边的“(” ,如果找不到"("就把“*”变为"("且"("尽可能要靠左边,然后从右往左匹配找到与"("匹配的")"如果找不到")"就把"*"变为“)” 且")" 要尽可能的靠右边
我们首先要预处理一下俩个数组 l和r 这个操作类似于差分于前缀和
l[i]:表示s[i]之前有多少个“(”
r[i]: 表示s[i]之后有多少个“)”;
code
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+10;
char s[N];
int l[N],r[N];
int main()
{
int t;
scanf("%d",&t);
while(t--){
bool flag1 =0,flag2=0;
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=0;i<=len+1;i++) l[i]=r[i]=0;
for(int i=1;i<=len;i++){
if(s[i]=='(') l[i]++;
else if(s[i]==')') r[i]++;
}
for(int i=1;i<=len;i++){//s[i]之前"("的数量
l[i]+=l[i-1];
}
for(int i=len;i>=1;i--){//s[i]之后")"的数量
r[i]+=r[i+1];
}
int x=0;
int start=1;
//从前往后遍历添加"("
for(int i=1;i<=len;i++){
if(s[i]==')'){
l[i]-=x;
if(l[i]>=1){
x++;
}
else{
int j;
for(j=start;j<i;j++){
if(s[j]=='*'){
s[j]='(';
start=j+1;
break;
}
}
if(j==i) flag1=1;
}
}
if(flag1){
break;
}
}
x=0;
int end=len;
//从后往前添加")"
for(int i=len;i>=1;i--){
if(s[i]=='('){
r[i]-=x;
if(r[i]>=1){
x++;
}
else{
int j;
for(j=end;j>i;j--){
if(s[j]=='*'){
s[j]=')';
end=j-1;
break;
}
}
if(j==i) flag2=1;
}
}
if(flag2) break;
}
if(flag1||flag2) printf("No solution!\n");
else {
for(int i=1;i<=len ;i++){
if(s[i]!='*') printf("%c",s[i]);
}
puts("");
}
}
return 0;
}
Little W and Contest
这个题参考了一下这个博客:
https://blog.youkuaiyun.com/njuptACMcxk/article/details/107641773?
题目:http://acm.hdu.edu.cn/showproblem.php?pid=6795
思路:这个题就是求 组成3个人互相不认识的队伍的 种类,先求最开始一条边都没加的情况数,然后建立一条关系,我们不能直接求加入这条关系后的情况数,观察可以发现,我们可以求得每次合并点减少的情况。
首先介绍一下:表示以u为根节点的图中权为1的点的个数 同理
接下来就要找减少的情况数:G[u] G[v] G[r]
1. 2 1 2
2 1 2 2
3 2 2 2
4 2 2 1
code:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=1e5+10;
const int mod=1e9+7;
ll p1[N],p2[N];
ll cnt[3];
int fa[N];
int n;
int w;
int get(int x ){
if(x==fa[x]) return x;
return fa[x]=get(fa[x]);
}
ll C2(ll x){
if(x<2) return 0;
return x*(x-1)/2%mod;
}
ll C3(ll x ){
if(x<3) return 0;
return x*(x-1)*(x-2)/6%mod;
}
int main()
{
int t;
scanf("%d",&t);
while(t--){
ll ans =0;
memset(cnt,0,sizeof(cnt));
scanf("%d",&n);
for(int i=1;i<=n;i++ ) fa[i]=i,p1[i]=0,p2[i]=0;
for(int i=1;i<=n;i++){
scanf("%d",&w);
if(w==1){
cnt[1]++;
p1[i]=1;
}
else{
cnt[2]++;
p2[i]=1;
}
}
ans=(C3(cnt[2])+(C2(cnt[2])*cnt[1])%mod)%mod;//最开始总的情况数
printf("%lld\n",ans);
for(int i=1;i<n;i++){
int u,v;
scanf("%d%d",&u,&v);
ll k=0;
int pu=get(u),pv=get(v);//要合并的俩个总共有四种减少的情况
k=(k+p2[pu]*p2[pv]*(cnt[2]-p2[pu]-p2[pv])%mod)%mod;
k=(k+p1[pu]*p2[pv]*(cnt[2]-p2[pu]-p2[pv])%mod)%mod;
k=(k+p2[pu]*p1[pv]*(cnt[2]-p2[pu]-p2[pv])%mod)%mod;
k=(k+p2[pu]*p2[pv]*(cnt[1]-p1[pu]-p1[pv])%mod)%mod;
ans=(ans-k+mod)%mod;
printf("%lld\n",ans);
fa[pv]=pu;
p2[pu]+=p2[pv],p1[pu]+=p1[pv];
p1[pv]=0,p2[pv]=0;
}
}
return 0;
}
Tokitsukaze and Rescue
http://acm.hdu.edu.cn/showproblem.php?pid=6797
思路:这是个求最短路的问题,只是有k段路被破坏了,现在问你怎么破坏这k段路才能使,1到n的距离最长
因为 k比较小,所以可以dfs枚举一下最短路中的每一段路径,把这一段这置为INF,每次更新ans即可
code:
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<vector>
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> PII;
const int N=55;
int G[N][N];
int dis[N];
bool st[N];
int n,k;
int pre[N];
int ans;
void dijkstra()
{
for(int i=1;i<=n;i++) dis[i]=INF, st[i]=0;
priority_queue<PII , vector<PII> ,greater<PII> > heap;
dis[1] = 0;
heap.push({0,1});
while(heap.size()){
auto t= heap.top();
heap.pop();
int ver=t.second,distance = t.first;
if(st[ver]) continue;
st[ver]=1;
for(int i=1;i<=n;i++){
if(dis[i]>distance+G[ver][i]){
dis[i]=distance+G[ver][i];
pre[i]= ver;
heap.push({dis[i],i});
}
}
}
}
void dfs(int num){
dijkstra();
if(num==k){
ans=max(ans,dis[n]);
return ;
}
vector<int> ve;
ve.push_back(n);
int x=n;
while(x!=1){//找到最短的那个路径
x=pre[x];
ve.push_back(x);
}
int w;
for(int i=0;i<ve.size()-1;i++) {
w=G[ve[i]][ve[i+1]];
G[ve[i]][ve[i+1]]=G[ve[i+1]][ve[i]]=INF;
dfs(num+1);
G[ve[i]][ve[i+1]]=G[ve[i+1]][ve[i]]=w;
}
}
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&k);
int a,b,c;
for(int i=1;i<=(n-1)*n/2;i++){
scanf("%d%d%d",&a,&b,&c);
G[a][b]=G[b][a]=c;
}
ans=0;
dfs(0);
printf("%d\n",ans);
}
return 0;
}