Andrew Stankevich Contest 2 (ASC 2) |我为什么要开这套题

6 篇文章 0 订阅
2 篇文章 0 订阅

F Roads

某个国家有石头路和烂泥路,石头路恰好是生成树,维护路自然是花钱的,现在希望通过修改路的维护费用使得石头路是最小生成树(可以不是唯一的MST),目标是总的修改量最小,要求输出方案。

设石头路的修改后权值为w[i]-d[i]
烂泥路的修改后权值为w[i]+d[i]
对于1条烂泥路x,恰对应了MST中一条链,显然它权值要大于上面的任意一条边y。
wx+dxwydy
dx+dywywx
原问题变为2分图最小权覆盖(Minimum Weighted Cover)
这可以用KM解决。

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) For(i,n-1) cout<<a[i]<<' '; cout<<a[n]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
#define MAXN 500
int fa[MAXN],wfa[MAXN],idefa[MAXN];
vi e[MAXN],wei[MAXN],id[MAXN];
int dep[MAXN]; 
void dfs(int x,int f,int d) {
    fa[x]=f;
    dep[x]=d;
    Rep(i,SI(e[x])) {
        int v=e[x][i],w=wei[x][i],idd=id[x][i];
        if (v^f){
            wfa[v]=w;
            idefa[v]=idd;
            dfs(v,x,d+1);
        }
    }
}
int f[MAXN][MAXN];
int u[MAXN],v[MAXN],w[MAXN];
namespace KM{
    const int N=405;
    const ll inf=~0U>>1;
    int n,nl,nr,m,z,py,x,y,i,j,p,lk[N],pre[N];
    bool vy[N];
    int lx[N],ly[N],d,w[N][N],slk[N];ll ans;
    int work(int nl,int nr){ // nl nr w
      n=max(nl,nr);
      For(i,nl) For(j,nr) w[i][j]=max(0,f[i][j]);
//    while(m--)scanf("%d%d%d",&x,&y,&z),w[y][x]=max(w[y][x],z);
      for(i=1;i<=n;i++)for(j=1;j<=n;j++)lx[i]=max(lx[i],w[i][j]);
      for(i=1;i<=n;i++){
        for(j=1;j<=n;j++)slk[j]=inf,vy[j]=0;
        for(lk[py=0]=i;lk[py];py=p){
          vy[py]=1;d=inf;x=lk[py];
          for(y=1;y<=n;y++)if(!vy[y]){
            if(lx[x]+ly[y]-w[x][y]<slk[y])slk[y]=lx[x]+ly[y]-w[x][y],pre[y]=py;
            if(slk[y]<d)d=slk[y],p=y;
          }
          for(y=0;y<=n;y++)if(vy[y])lx[lk[y]]-=d,ly[y]+=d;else slk[y]-=d;
        }
        for(;py;py=pre[py])lk[py]=lk[pre[py]];
      }
      for(i=1;i<=n;i++)ans+=lx[i]+ly[i];
//    printf("%lld\n",ans);
//    for(i=1;i<=nl;i++)printf("%d ",w[lk[i]][i]?lk[i]:0);
    }
}
int n,m;
void prekm(int x,int y,int p) {
    if (dep[x]<dep[y]) swap(x,y);
    while(dep[x]>dep[y]) {
        f[idefa[x]][p]=-w[p+n-1]+wfa[x];
        x=fa[x];
    }
    while(x^y) {
        f[idefa[x]][p]=-w[p+n-1]+wfa[x];
        f[idefa[y]][p]=-w[p+n-1]+wfa[y];
        x=fa[x];
        y=fa[y];
    }

}
int main()
{
    freopen("roads.in","r",stdin);
    freopen("roads.out","w",stdout);
    n=read(),m=read();
    For(i,n-1) {
        int u=read(),v=read(),w=read();
        e[u].pb(v);e[v].pb(u);
        wei[u].pb(w);wei[v].pb(w);
        id[u].pb(i); id[v].pb(i);
        ::w[i]=w;::u[i]=u;::v[i]=v;
    }
    dfs(1,0,1);
    MEM(f)
    Fork(i,n,m) {
        u[i]=read(),v[i]=read();w[i]=read();
        prekm(u[i],v[i],i-n+1);
    }
    KM::work(n-1,m-n+1);
    For(i,n-1) {
        printf("%d\n",w[i]-KM::lx[i]);
    }
    Fork(i,n,m) {
        printf("%d\n",w[i]+KM::ly[i-n+1]);
    }

    return 0;

}

H

一张票有N*M的黑白格子,可以卷成圆柱(cylinder)再卷成环,问这样操作后依然不同构的票有多少种。(n,m<=20)

根据伯恩赛德定理,等价类个数=置换集合中的每个置换不动点个数C(f)的平均值.
当n!=m时,有左移,上移,转180度,3种置换(互不影响)。
等价类个数= 2nm
这题n=m时,还可以旋转90度。
等价类个数= 4nm

#include<bits/stdc++.h> 
using namespace std;
#define For(i,n) for(int i=1;i<=n;i++)
#define Fork(i,k,n) for(int i=k;i<=n;i++)
#define ForkD(i,k,n) for(int i=n;i>=k;i--)
#define Rep(i,n) for(int i=0;i<n;i++)
#define ForD(i,n) for(int i=n;i;i--)
#define RepD(i,n) for(int i=n;i>=0;i--)
#define Forp(x) for(int p=pre[x];p;p=next[p])
#define Forpiter(x) for(int &p=iter[x];p;p=next[p])  
#define Lson (o<<1)
#define Rson ((o<<1)+1)
#define MEM(a) memset(a,0,sizeof(a));
#define MEMI(a) memset(a,0x3f,sizeof(a));
#define MEMi(a) memset(a,128,sizeof(a));
#define MEMx(a,b) memset(a,b,sizeof(a));
#define INF (0x3f3f3f3f)
#define F (1000000007)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define vi vector<int> 
#define pi pair<int,int>
#define SI(a) ((a).size())
#define Pr(kcase,ans) printf("Case #%d: %lld\n",kcase,ans);
#define PRi(a,n) Rep(i,n-1) cout<<a[i]<<' '; cout<<a[n-1]<<endl;
#define PRi2D(a,n,m) For(i,n) { \
                        For(j,m-1) cout<<a[i][j]<<' ';\
                        cout<<a[i][m]<<endl; \
                        } 
#pragma comment(linker, "/STACK:102400000,102400000")
#define ALL(x) (x).begin(),(x).end()
#define gmax(a,b) a=max(a,b);
#define gmin(a,b) a=min(a,b);
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
ll mul(ll a,ll b){return (a*b)%F;}
ll add(ll a,ll b){return (a+b)%F;}
ll sub(ll a,ll b){return ((a-b)%F+F)%F;}
void upd(ll &a,ll b){a=(a%F+b%F)%F;}
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(!isdigit(ch)) {if (ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) { x=x*10+ch-'0'; ch=getchar();}
    return x*f;
} 
int gcd(int a,int b){
    return (!b)?a:gcd(b,a%b);
}
struct BigInteger {
    typedef unsigned long long LL;

    static const int BASE = 100000000;
    static const int WIDTH = 8;
    vector<int> s;

    BigInteger& clean(){while(!s.back()&&s.size()>1)s.pop_back(); return *this;}
    BigInteger(LL num = 0) {*this = num;}
    BigInteger(string s) {*this = s;}
    BigInteger& operator = (long long num) {
        s.clear();
        do {
            s.push_back(num % BASE);
            num /= BASE;
        } while (num > 0);
        return *this;
    }
    BigInteger& operator = (const string& str) {
        s.clear();
        int x, len = (str.length() - 1) / WIDTH + 1;
        for (int i = 0; i < len; i++) {
            int end = str.length() - i*WIDTH;
            int start = max(0, end - WIDTH);
            sscanf(str.substr(start,end-start).c_str(), "%d", &x);
            s.push_back(x);
        }
        return (*this).clean();
    }

    BigInteger operator + (const BigInteger& b) const {
        BigInteger c; c.s.clear();
        for (int i = 0, g = 0; ; i++) {
            if (g == 0 && i >= s.size() && i >= b.s.size()) break;
            int x = g;
            if (i < s.size()) x += s[i];
            if (i < b.s.size()) x += b.s[i];
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c;
    }
    BigInteger operator - (const BigInteger& b) const {
        assert(b <= *this); // ¼õÊý²»ÄÜ´óÓÚ±»¼õÊý
        BigInteger c; c.s.clear();
        for (int i = 0, g = 0; ; i++) {
            if (g == 0 && i >= s.size() && i >= b.s.size()) break;
            int x = s[i] + g;
            if (i < b.s.size()) x -= b.s[i];
            if (x < 0) {g = -1; x += BASE;} else g = 0;
            c.s.push_back(x);
        }
        return c.clean();
    }
    BigInteger operator * (const BigInteger& b) const {
        int i, j; LL g;
        vector<LL> v(s.size()+b.s.size(), 0);
        BigInteger c; c.s.clear();
        for(i=0;i<s.size();i++) for(j=0;j<b.s.size();j++) v[i+j]+=LL(s[i])*b.s[j];
        for (i = 0, g = 0; ; i++) {
            if (g ==0 && i >= v.size()) break;
            LL x = v[i] + g;
            c.s.push_back(x % BASE);
            g = x / BASE;
        }
        return c.clean();
    }
    BigInteger operator / (const BigInteger& b) const {
        assert(b > 0);  // ³ýÊý±ØÐë´óÓÚ0
        BigInteger c = *this;       // ÉÌ:Ö÷ÒªÊÇÈÃc.sºÍ(*this).sµÄvectorÒ»Ñù´ó
        BigInteger m;               // ÓàÊý:³õʼ»¯Îª0
        for (int i = s.size()-1; i >= 0; i--) {
            m = m*BASE + s[i];
            c.s[i] = bsearch(b, m);
            m -= b*c.s[i];
        }
        return c.clean();
    }
    BigInteger operator % (const BigInteger& b) const { //·½·¨Óë³ý·¨Ïàͬ
        BigInteger c = *this;
        BigInteger m;
        for (int i = s.size()-1; i >= 0; i--) {
            m = m*BASE + s[i];
            c.s[i] = bsearch(b, m);
            m -= b*c.s[i];
        }
        return m;
    }
    // ¶þ·Ö·¨ÕÒ³öÂú×ãbx<=mµÄ×î´óµÄx
    int bsearch(const BigInteger& b, const BigInteger& m) const{
        int L = 0, R = BASE-1, x;
        while (1) {
            x = (L+R)>>1;
            if (b*x<=m) {if (b*(x+1)>m) return x; else L = x;}
            else R = x;
        }
    }
    BigInteger& operator += (const BigInteger& b) {*this = *this + b; return *this;}
    BigInteger& operator -= (const BigInteger& b) {*this = *this - b; return *this;}
    BigInteger& operator *= (const BigInteger& b) {*this = *this * b; return *this;}
    BigInteger& operator /= (const BigInteger& b) {*this = *this / b; return *this;}
    BigInteger& operator %= (const BigInteger& b) {*this = *this % b; return *this;}

    bool operator < (const BigInteger& b) const {
        if (s.size() != b.s.size()) return s.size() < b.s.size();
        for (int i = s.size()-1; i >= 0; i--)
            if (s[i] != b.s[i]) return s[i] < b.s[i];
        return false;
    }
    bool operator >(const BigInteger& b) const{return b < *this;}
    bool operator<=(const BigInteger& b) const{return !(b < *this);}
    bool operator>=(const BigInteger& b) const{return !(*this < b);}
    bool operator!=(const BigInteger& b) const{return b < *this || *this < b;}
    bool operator==(const BigInteger& b) const{return !(b < *this) && !(b > *this);}
};

ostream& operator << (ostream& out, const BigInteger& x) {
    out << x.s.back();
    for (int i = x.s.size()-2; i >= 0; i--) {
        char buf[20];
        sprintf(buf, "%08d", x.s[i]);
        for (int j = 0; j < strlen(buf); j++) out << buf[j];
    }
    return out;
}

istream& operator >> (istream& in, BigInteger& x) {
    string s;
    if (!(in >> s)) return in;
    x = s;
    return in;
}
BigInteger p2[410];

#define MAXN (10000)
int a[MAXN];
void pre(int n){
    Rep(i,n) a[i]=i;
}
bool vis[MAXN];
BigInteger polya(int k,int *a,int n) { //Öû»²»¶¯µã¸öÊý 
    Rep(i,n) vis[i]=0;
    int p=0;
    Rep(i,n) if (!vis[i]) {
        int j=i;
        do {
            vis[j]=1;
            j=a[j];
        }while(!vis[j]);
        ++p;
    }
    return p2[p];
}

int a2[MAXN];



int main()
{
    freopen("tickets.in","r",stdin);
    freopen("tickets.out","w",stdout);
    int n=read(),m=read();
    pre(n*m);
    p2[1]=2;
    Fork(i,2,n*m) p2[i]=p2[i-1]*2;
    BigInteger ans=0;
    pre(n*m);
    Rep(i2,n) {
        Rep(j2,m) {
            Rep(i,n) Rep(j,m)
                a[i*m+j] = (i2+i)%n*m+(j+j2)%m;
            ans+=polya(2,a,n*m);
            if (n==m) {
                Rep(i,n) Rep(j,m) {
                    a2[i*m+j] = a[(n-j-1)*m+i];
                }
                ans+=polya(2,a2,n*m);
            }
            Rep(i,n) Rep(j,m) {
                a[(n-i-1)*m+(m-j-1)] = (i2+i)%n*m+(j+j2)%m;
            }
            ans+=polya(2,a,n*m);
            if (n==m) {
                Rep(i,n) Rep(j,m) {
                    a2[i*m+j] = a[(n-j-1)*m+i];
                }
                ans+=polya(2,a2,n*m);
            }
        }
    }
    ans/=(n*m*2);
    if (n==m) ans/=2;
    cout<<ans<<endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值