弱校聯萌十一大決戰之強力熱身

必須吐槽一下……這場比賽真是原題大戰……我一開始只看出來幾道題是SCU上的……後來有人我告訴我是2015四川賽區的一整套題……出題人是厲害……

A.Easy Math

比賽的時候做一個大膽地猜想……只有每個數開方後都是整數它們的和才都是整數。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
#define pa pair<int,int>

using namespace std;

int main(){
    int n,x;
	while (scanf("%d",&n)!=EOF){
		for (int i=1;i<=n;i++)
			scanf("%d",&x);
			bool flag=1;
			if (sqrt(x)-floor(sqrt(x))>0.0) {
				flag=0;
			}
    if (flag) puts("Yes");
	else puts("No");
	}
	return 0;
}

B.Carries

注意到0≤ai≤10^9,這樣只需要枚舉在第10^k有多少個數產生進位就可以了。 
注意到答案和順序無關,只需要保證一對數不能計重就行,也就是說排序不影響結果。 
那麼枚舉k,令s=10^k 。
每次對數組都mod s ,然後排序。

題目轉化爲:現在已知a< s, b < s,現在已知a,求有多少個b使得a+b>=s。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16

using namespace std;

const int MAXN=1e+5+1000;
int n;
LL A[MAXN],B[MAXN];

int main(){
	while (~scanf("%d",&n)){
		for (int i=0;i<n;i++) scanf("%lld",&A[i]);
	sort(A,A+n);
	LL cnt=0,s=10;
	for (int k=1;k<=10;k++,s*=10){
		for (int i=0;i<n;i++) B[i]=A[i]%s;
		sort(B,B+n);
		for (int i=0;i<n;i++){
			LL t=s-B[i];
			if (B[n-1]>=t){
				int pos=lower_bound(B,B+n,t)-B;
				if (pos>i) cnt+=n-pos;
				else cnt+=n-(i+1);
				}
			}
		}
		printf("%lld\n",cnt);
	}
	return 0;
}


C.Censor

套用KMP模板求得失配數組,再開一個棧保存沒有被刪除的字符以及已經匹配了a字符串的字符個數。如果在b中完整匹配到了a,則把length(a)個字符出棧,然後取棧頂保存的已匹配的字符個數繼續去匹配a,棧爲空則從0開始。複雜度O(n)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16

using namespace std;

const int N = 5e6+5;   
  
struct PP {  
    char c;  
    int cnt;  
}sta[N];  
  
int f[N];  
char w[N], p[N], res[N];  
int wlen, plen; 
int hehe(){
	int cur=0,cnt=0;
	for (int i=0;i<plen;i++){
		while (cnt && w[cnt]!=p[i]) cnt=f[cnt];
		if (w[cnt]==p[i]){
			sta[cur++]=(PP){p[i],cnt+1};
			cnt++;
		}
		else {
			sta[cur++]=(PP){p[i],0};
			cnt=0;
		}
		if (cnt==wlen){
			cur-=wlen;
			if (cur==0) cnt=0;
			else cnt=sta[cur-1].cnt;
		}
	}
	return cur;
}
void getFail(char *p, int *f, int m) {  
    f[0]=0, f[1]=0;  
    for(int i = 1;i < m; i++) {  
        int j = f[i];  
        while(j && p[i] != p[j]) j = f[j];  
        f[i+1] = (p[i] == p[j])?j+1:0;  
    }  
}  
void solve(){
	wlen=strlen(w),plen=strlen(p);
	getfail(w,f,wlen);
	int num=hehe();
	for (int i=0;i<num;i++)
		putchar(sta[i].c);
	puts("");
}
int main(){
	while(scanf("%s%s",w,p)!=EOF)
		solve();
	return 0;
}

D.Vertex Cover

只搜索前30個點的選與不選,如果還有邊沒有被覆蓋則一定選非前30的點來覆蓋。這樣一定可以搜索到最優解。

如果一個點不選則起相鄰的點必選。

所以我們初始化所有的點都選,然後搜索哪些點不選。

當我們搜索到一個點的時候,判斷它相鄰的點是否有不選的,若有則必選;否則我們可以選擇不選該點。這樣搜索出來的狀態對於前30個點之間的邊來說一定是合法的。再加上最優性剪枝就可過這題。

搜索的做法:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16

using namespace std;

struct node{
	int to,next;
}E[550*550*2];
pair<int,int>a[35];
bool use[35],tu[35][35];
int N,ans,du[550],flag,fuck[550];
int head[550],num;
int n,m;

void prepare(){
	memset(head,-1,sizeof(head));
	num=0;
}
void add(int u,int v){
	E[num].to=v;
	E[num].next=head[u];
	head[u]=num++;

	E[num].to=u;
	E[num].next=head[v];
	head[v]=num++;
}
void dfs(int id,int val){
	if (val+flag>ans) return ;
	if (id==N+1){
		ans=min(ans,val+flag);
		return ;
	}
	bool ok=1;
	int w;
	for (int i=1;i<=N;i++){
		if (tu[id][i] && use[i]) ok=0;
	}
	if (ok){
		use[id]=1;
		for (int i=head[id];i+1;i=E[i].next){
			w=E[i].to;
			if (fuck[w]==0) flag++;
			fuck[w]++;
		}
		dfs(id+1,val);
		for (int i=head[id];i+1;i=E[i].next){
			w=E[i].to;
			fuck[w]--;
			if  (fuck[w]==0) flag--;
		}
		use[id]=0;
	}
	dfs(id+1,val+1);
}
int main(){
	int i,u,v;
	while (scanf("%d%d",&n,&m)!=EOF){
		prepare();
		memset(tu,false,sizeof(tu));
		memset(fuck,0,sizeof(fuck));
		memset(du,0,sizeof(du));
		for (int i=1;i<=m;i++){
			scanf("%d%d",&u,&v);
			if (u>30 || v>30) add(u,v);
			du[u]++, du[v]++;
			if (u<=30 || v<=30)
				tu[u][v]=tu[v][u]=1;
		}
		flag=0;
		N=min(30,n);
		memset(use,false,sizeof(use));
		ans=m;
		dfs(1,0);
		printf("%d\n",ans);
	}
	return 0;
}


但是這道題,一看上去就是最小頂點覆蓋問題,而最小定點覆蓋問題等於二分圖最大匹配,所以寫個匈牙利把答案/2也能過。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16

using namespace std;

int n,m;
int link[505],mp[505][505];
bool used[505];

bool dfs(int u){
	int v;
	for (v=1;v<=n;v++)
		if (mp[u][v] && !used[v]){
			used[v]=1;
			if (link[v]==-1 || dfs(link[v])){
				link[v]=u;
				return true;
			}
		}
	return false;
}
int hungary(){
	int res=0;
	int u;
	memset(link,-1,sizeof(link));
	for (u=1;u<=n;u++){
		memset(used,0,sizeof(used));
		if (dfs(u)) {res++;}
	}
	return res;
}
int main(){
    while(scanf("%d%d",&n,&m)!=EOF){
        memset(mp,0,sizeof(mp));
		for (int i=1;i<=m;i++){
			int u,v;
			scanf("%d%d",&u,&v);
			mp[u][v]=1;
			mp[v][u]=1;
		}
		printf("%d\n",hungary()/2);
	}
}

E.Rectangle

一道思考題,推推結論,找找規律就好了。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16

using namespace std;
LL n,m,k;
int main()
{
    while(~scanf("%lld%lld%lld",&n,&m,&k)){
        LL ans=0;
        for(LL x=1;x<=n;x++){
            LL tmax=(k-2*x)/2;
            if(tmax>0){
                if(tmax>=m)tmax=m;
                if(tmax%2==0){
                    ans+=(n-x+1)*(m+m-tmax+1)*(tmax/2);
                }
                else {
                    ans+=(n-x+1)*((m+m-tmax+1)/2)*tmax;
                }
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}


F.Necklace

枚舉值爲10000的數。確定值爲10000的數的位置後,環就斷成了一條鏈。我們可以用動態規劃求出(1,i)的數的不增序列的最大和,同理求出(i,n)的不增序列的最大和。然後枚舉斷點即可。在動態規劃的過程中需要用到樹狀數組優化轉移(線段樹會T)。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<ctime>
#define LL long long
#define db double
#define EPS 1e-1
#define inf 1e16
const int nn = 110000;
int n;
int a[nn];
int c[nn*2];
int f1[nn],f2[nn];
int tree[11000];
inline int lowbit(int x)
{
    return x&(-x);
}
int getmax(int id)
{
    int re=0;
    while(id>0)
    {
        re=max(re,tree[id]);
        id-=lowbit(id);
    }
    return re;
}
void add(int id,int val)
{
    if(id==0)
        return ;
    for(int i=id;i<=10000;i+=lowbit(i))
    {
        tree[i]=max(tree[i],val);
    }
}
int solve(int id)
{
    int i;
    f1[0]=0;
    for(i=1;i<=10000;i++)
        tree[i]=0;
    int tem;
    for(i=id+1;i<id+n;i++)
    {
        if(c[i]==10000)
        {
            f1[i-id]=f1[i-id-1];
            continue;
        }
        tem=getmax(10000-c[i]+1)+c[i];
        f1[i-id]=max(f1[i-id-1],tem);
        add(10000-c[i]+1,tem);
    }
    f2[n]=0;
    for(i=1;i<=10000;i++)
        tree[i]=0;
    for(i=id+n-1;i>=id+1;i--)
    {
        if(c[i]==10000)
        {
            f2[i-id]=f2[i-id+1];
            continue;
        }
        tem=getmax(10000-c[i]+1)+c[i];
        f2[i-id]=max(f2[i-id+1],tem);
        add(10000-c[i]+1,tem);
    }
    int re=0;
    for(i=0;i<=n-1;i++)
        re=max(re,f1[i]+f2[i+1]);
    return re;
}
int ve[20],lv;
int main()
{
    int i;
    while(scanf("%d",&n)!=EOF)
    {
        lv=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            c[i]=a[i];
            c[i+n]=a[i];
            if(a[i]==10000)
                ve[lv++]=i;
        }
        int ans=0;
        for(i=0;i<lv;i++)
        {
            ans=max(ans,solve(ve[i]));
        }
        printf("%d\n",ans+10000);
    }
    return 0;
}

G.Party

神建圖的一道題


H.Range Query

完備匹配的最小字典序。  先找出最大匹配,然後枚舉從小到每個點枚舉最優狀態,刪邊然後繼續尋找增廣路。

我抄的這道題代碼……因爲帶字典序我就不會做了……畢竟太弱了(結果居然拿了個FB)。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int mmax  = 60;

int max_num[mmax],min_num[mmax];
int L[mmax],R[mmax];
bool G[mmax][mmax];
int n,m1,m2;
void init()
{
    memset(G,0,sizeof G);
    for(int i=1;i<=n;i++)
    {
        L[i]=min_num[i]=1;
        R[i]=max_num[i]=n;
    }
}
int link[mmax];
bool vis[mmax];
int Match[mmax];
bool match(int x)
{
    for(int i=1;i<=n;i++)
    {
        if(G[x][i] && !vis[i])
        {
            vis[i]=1;
            if(link[i]==-1 || match(link[i]))
            {
                link[i]=x;
                Match[x]=i;
                return 1;
            }
        }
    }
    return 0;
}
int hungury()
{
    int cnt=0;
    memset(link,-1,sizeof link);
    for(int i=1;i<=n;i++)
    {
        memset(vis,0,sizeof vis);
        if(match(i))
            cnt++;
    }
    return cnt;
}
int main()
{
    int a,b,c;
    while(~scanf("%d %d %d",&n,&m1,&m2))
    {
        init();
        for(int i=1;i<=m1;i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            for(int j=a;j<=b;j++)
                min_num[j]=max(min_num[j],c);
            L[c]=max(L[c],a);
            R[c]=min(R[c],b);
        }
        for(int i=1;i<=m2;i++)
        {
            scanf("%d %d %d",&a,&b,&c);
            for(int j=a;j<=b;j++)
                max_num[j]=min(max_num[j],c);
            L[c]=max(L[c],a);
            R[c]=min(R[c],b);
        }

        for(int i=1;i<=n;i++)
        {
            for(int j=min_num[i];j<=max_num[i];j++)
            {
                if(L[j]<=i && i<=R[j])
                    G[i][j]=1;
            }
        }
        int num = hungury();
        if(num==n)
        {

//            for(int i=1;i<=n;i++)
//                printf("%d%c",Match[i],i==n?'\n':' ');

            for(int i=1;i<=n;i++)
            {
                int tmp=Match[i];
                G[i][tmp]=0;
                link[tmp]=-1;
                bool fg=0;
                memset(vis,0,sizeof vis);
                for(int j=1;j<tmp;j++)
                {
                    if(!vis[j] && G[i][j])
                    {
                        vis[j]=1;
                        if(link[j]==-1 || match(link[j]))
                        {
                            link[j]=i;
                            Match[i]=j;
                            fg=1;
                            break;
                        }
                    }
                }
                if(!fg)
                {
                    G[i][tmp]=1;
                    link[tmp]=i;
                }
                tmp=Match[i];
                for(int j=1;j<=n;j++)
                    G[j][tmp]=0;
            }


            for(int i=1;i<=n;i++)
                printf("%d%c",Match[i],i==n?'\n':' ');
        }
        else
            puts("-1");
    }
    return 0;
}

I.Travel

不會,不想補了,POI2007原題。


J.Right turn

模擬題。

對於出現過的每一個x建立一個set 集合,對於y也如此。然後查找要走到哪個點即可,主要要對狀態記錄,判斷是否無限循環。

#include <set>
#include <map>
#include <cmath>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long int LL;
const int M = 1009,INF = 0x3fffffff;
vector<pair<int, vector<int> > > vx, vy;
struct place {int x, y ,dir;}s;
vector<struct place> repeat;

bool operator == (struct place a, struct place b) {
    return a.x == b.x && a.y == b.y && a.dir == b.dir;
}

void input(int n) {
    for (int i = 0; i < n; i++) {
        int x, y;
        scanf("%d%d", &x, &y);
        bool key = true;
        for (int j = 0; j < vx.size(); j++) {
            if (vx[j].first == x) {
                vx[j].second.push_back(y);
                key = false;
                break;
            }
        }
        if (key) {
            pair<int, vector<int> >p;
            p.first = x;
            p.second.push_back(y);
            vx.push_back(p);
        }
        key = true;
        for (int j = 0; j < vy.size(); j++) {
            if (vy[j].first == y) {
                vy[j].second.push_back(x);
                key = false;
                break;
            }
        }
        if (key) {
            pair<int, vector<int> >p;
            p.first = y;
            p.second.push_back(x);
            vy.push_back(p);
        }
    }
}

bool move(void) {
    if (s.dir == 0) {
        for (int i = 0; i < vy.size(); i++) {
            if (vy[i].first == s.y) {
                int temp = INF;
                bool ok = false;
                for (int j = 0; j < vy[i].second.size(); j++) {
                    if(vy[i].second.at(j) > s.x && vy[i].second.at(j) < temp) {
                        temp = vy[i].second.at(j);
                        ok = true;
                    }
                }
                s.x = temp - 1;
                s.dir = 1;
                return ok;
            }
        }
    }
    if (s.dir == 1) {
        for (int i = 0; i < vx.size(); i++) {
            if (vx[i].first == s.x) {
                int temp = -INF;
                bool ok = false;
                for (int j = 0; j < vx[i].second.size(); j++) {
                    if(vx[i].second.at(j) < s.y && vx[i].second.at(j) > temp) {
                        temp = vx[i].second.at(j);
                        ok = true;
                    }
                }
                s.y = temp + 1;
                s.dir = 2;
                return ok;
            }
        }
    }
    if (s.dir == 2) {
        for (int i = 0; i < vy.size(); i++) {
            if (vy[i].first == s.y) {
                int temp = -INF;
                bool ok = false;
                for (int j = 0; j < vy[i].second.size(); j++) {
                    if(vy[i].second.at(j) < s.x && vy[i].second.at(j) > temp) {
                        temp = vy[i].second.at(j);
                        ok = true;
                    }
                }
                s.x = temp + 1;
                s.dir = 3;
                return ok;
            }
        }
    }
    if (s.dir == 3) {
        for (int i = 0; i < vx.size(); i++) {
            if (vx[i].first == s.x) {
                int temp = INF;
                bool ok = false;
                for (int j = 0; j < vx[i].second.size(); j++) {
                    if(vx[i].second.at(j) > s.y && vx[i].second.at(j) < temp) {
                        temp = vx[i].second.at(j);
                        ok = true;
                    }
                }
                s.y = temp - 1;
                s.dir = 0;
                return ok;
            }
        }
    }
    return false;
}

bool judge_infinite(void) {
    for (int i = 0; i < repeat.size(); i++) {
        if (s == repeat[i]) return true;
    }
    return false;
}

int main(void) {
    //problem: soj4445, address:http://acm.scu.edu.cn/soj/problem.action?id=4445
    int n;
    while (~scanf("%d", &n)) {
        vx.clear();
        vy.clear();
        repeat.clear();
        input(n);
        s.x = s.y = s.dir = 0;
        for (int i = 0;; i++) {
            repeat.push_back(s);
            if (!move()) {
                printf("%d\n", i);
                break;
            }
            if (judge_infinite()) {
                printf("-1\n");
                break;
            }
        }
    }
    return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章